From 31b576e57016b90ac5e496b40882b4651c49ae6e Mon Sep 17 00:00:00 2001 From: akinross Date: Thu, 26 Oct 2023 19:10:58 +0200 Subject: [PATCH] [minor_change] add support for configuration of fabric pod selectors with aci_fabric_pod_selector module --- plugins/module_utils/constants.py | 2 + plugins/modules/aci_fabric_pod_selector.py | 406 ++++++++++++++++++ .../targets/aci_fabric_pod_selector/aliases | 2 + .../aci_fabric_pod_selector/tasks/main.yml | 305 +++++++++++++ 4 files changed, 715 insertions(+) create mode 100644 plugins/modules/aci_fabric_pod_selector.py create mode 100644 tests/integration/targets/aci_fabric_pod_selector/aliases create mode 100644 tests/integration/targets/aci_fabric_pod_selector/tasks/main.yml diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index b6ef1236f..b8b2d7c4c 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -94,3 +94,5 @@ MATCH_GRACEFUL_RESTART_CONTROLS_MAPPING = dict(helper="helper", complete="") EP_LOOP_PROTECTION_ACTION_MAPPING = {"bd": "bd-learn-disable", "port": "port-disable"} + +FABRIC_POD_SELECTOR_TYPE_MAPPING = dict(all="ALL", range="range") diff --git a/plugins/modules/aci_fabric_pod_selector.py b/plugins/modules/aci_fabric_pod_selector.py new file mode 100644 index 000000000..6c95f32df --- /dev/null +++ b/plugins/modules/aci_fabric_pod_selector.py @@ -0,0 +1,406 @@ +#!/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_fabric_pod_selector +short_description: Manage Fabric Pod Selectors (fabric:PodS) +description: +- Manage Fabric Pod Selectors on Cisco ACI fabrics. +options: + pod_profile: + description: + - The name of the Pod Profile that contains the Selector. + type: str + name: + description: + - The name of the Pod Selector. + type: str + aliases: [ selector, pod_selector ] + description: + description: + - The description for the Fabric Pod Selector. + type: str + aliases: [ descr ] + type: + description: + - The type of the Pod Selector. + type: str + choices: [ all, range ] + blocks: + description: + - The pod id(s) associated with the Pod Selector. + - Existing blocks will be removed when they are not matching provided blocks. + - A comma-separated string of pod ids or ranges of pod ids. (ex. 1,3-4) + type: str + aliases: [ pod_id, pod_id_range ] + policy_group: + description: + - The Fabric Policy Group to bind to this Pod Selector. + - Provide an empty string C("") to remove the Fabric Policy Group binding. + 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 + name_alias: + description: + - The alias for the current object. This relates to the nameAlias field in ACI. + type: str +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation +- cisco.aci.owner + +notes: +- The C(pod_profile) must exist before using this module in your playbook. +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fabric:PodS). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Tim Cragg (@timcragg) +- Akini Ross (@akinross) +""" + +# TODO add to notes section when cisco.aci.aci_pod_profile is implemented: +# The M(cisco.aci.aci_pod_profile) module can be used for this. + +EXAMPLES = r""" +- name: Add a new pod selector with type all + cisco.aci.aci_fabric_pod_selector: + host: apic + username: admin + password: SomeSecretPassword + pod_profile: default + name: ans_pod_selector + type: all + policy_group: ansible_policy_group + state: present + delegate_to: localhost + +- name: Add a new pod selector with type range and blocks + cisco.aci.aci_fabric_pod_selector: + host: apic + username: admin + password: SomeSecretPassword + pod_profile: default + name: ans_pod_selector + type: range + blocks: 1,3-4 + policy_group: ansible_policy_group + state: present + delegate_to: localhost + +- name: Remove a policy_group from an existing pod selector + cisco.aci.aci_fabric_pod_selector: + host: apic + username: admin + password: SomeSecretPassword + pod_profile: default + type: all + name: ans_pod_selector + policy_group: "" + state: present + delegate_to: localhost + +- name: Remove a pod selector type all + cisco.aci.aci_fabric_pod_selector: + host: apic + username: admin + password: SomeSecretPassword + pod_profile: default + type: all + name: ans_pod_selector + state: absent + delegate_to: localhost + +- name: Remove a pod selector type range + cisco.aci.aci_fabric_pod_selector: + host: apic + username: admin + password: SomeSecretPassword + pod_profile: default + type: range + name: ans_pod_selector + state: absent + delegate_to: localhost + +- name: Query a pod selector + cisco.aci.aci_fabric_pod_selector: + host: apic + username: admin + password: SomeSecretPassword + pod_profile: default + name: ans_pod_selector + type: all + state: query + delegate_to: localhost + register: query_result + +- name: Query all pod selectors + cisco.aci.aci_fabric_pod_selector: + 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 binascii +import os + +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 FABRIC_POD_SELECTOR_TYPE_MAPPING + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + description=dict(type="str", aliases=["descr"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + name_alias=dict(type="str"), + pod_profile=dict(type="str"), + name=dict(type="str", aliases=["selector", "pod_selector"]), + type=dict(type="str", choices=["all", "range"]), + blocks=dict(type="str", aliases=["pod_id", "pod_id_range"]), + policy_group=dict(type="str"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["pod_profile", "name", "type"]], + ["state", "present", ["pod_profile", "name", "type"]], + ], + ) + + aci = ACIModule(module) + + name_alias = module.params.get("name_alias") + pod_profile = module.params.get("pod_profile") + name = module.params.get("name") + policy_group = module.params.get("policy_group") + description = module.params.get("description") + selector_type = FABRIC_POD_SELECTOR_TYPE_MAPPING.get(module.params.get("type")) + blocks = [i.strip().split("-") for i in module.params.get("blocks").split(",")] if module.params.get("blocks") else [] + state = module.params.get("state") + + if state == "present" and selector_type == "range" and not blocks: + module.fail_json(msg="The 'blocks' parameter is required when the 'type' parameter is set to 'range' and 'state' parameter is set to 'present'.") + + child_classes = ["fabricRsPodPGrp", "fabricPodBlk"] + + aci.construct_url( + root_class=dict( + aci_class="fabricPodP", + aci_rn="fabric/podprof-{0}".format(pod_profile), + module_object=pod_profile, + target_filter={"name": pod_profile}, + ), + subclass_1=dict( + aci_class="fabricPodS", + aci_rn="pods-{0}-typ-{1}".format(name, selector_type), + module_object=name, + target_filter={"name": name, "type": selector_type}, + ), + child_classes=child_classes, + ) + + aci.get_existing() + + if state == "present": + child_configs = [] + + if policy_group is not None: + child_configs.append( + { + "fabricRsPodPGrp": { + "attributes": {"status": "deleted"} if policy_group == "" else {"tDn": "uni/fabric/funcprof/podpgrp-{0}".format(policy_group)} + } + } + ) + + if blocks: + if isinstance(aci.existing, list) and len(aci.existing) > 0: + for child in aci.existing[0].get("fabricPodS", {}).get("children", {}): + if child.get("fabricPodBlk"): + from_ = child.get("fabricPodBlk").get("attributes").get("from_") + to_ = child.get("fabricPodBlk").get("attributes").get("to_") + if [from_, to_] in blocks: + blocks.remove([from_, to_]) + elif (from_ == to_) and [from_] in blocks: + blocks.remove([from_]) + else: + child_configs.append( + { + "fabricPodBlk": { + "attributes": { + "dn": "uni/fabric/podprof-{0}/pods-{1}-typ-{2}/podblk-{3}".format( + pod_profile, name, selector_type, child.get("fabricPodBlk").get("attributes").get("name") + ), + "status": "deleted", + } + } + } + ) + + for block in blocks: + child_configs.append( + { + "fabricPodBlk": { + "attributes": { + "name": binascii.b2a_hex(os.urandom(8)).decode("utf-8"), + "from_": block[0], + "to_": block[1] if len(block) > 1 else block[0], + } + } + } + ) + + aci.payload( + aci_class="fabricPodS", + class_config=dict( + name=name, + descr=description, + nameAlias=name_alias, + type=selector_type, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class="fabricPodS") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/aci_fabric_pod_selector/aliases b/tests/integration/targets/aci_fabric_pod_selector/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_fabric_pod_selector/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_fabric_pod_selector/tasks/main.yml b/tests/integration/targets/aci_fabric_pod_selector/tasks/main.yml new file mode 100644 index 000000000..ca9fb8209 --- /dev/null +++ b/tests/integration/targets/aci_fabric_pod_selector/tasks/main.yml @@ -0,0 +1,305 @@ +# Test code for the ACI modules +# 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) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +# CLEAN EXISTING ENVIRONMENT +- name: Ensure Fabric Pod Selectors are absent before testing + cisco.aci.aci_fabric_pod_selector: + <<: *aci_info + name: "{{ item.name }}" + pod_profile: default + type: "{{ item.type }}" + state: absent + loop: + - {name: ansible_fabric_pod_pol_sel_1, type: all} + - {name: ansible_fabric_pod_pol_sel_2, type: range} + - {name: ansible_fabric_pod_pol_sel_3, type: range} + - {name: default, type: all} + +- name: Ensure a Pod Policy Group exists before testing + cisco.aci.aci_fabric_pod_policy_group: + <<: *aci_info + name: ansible_fabric_pod_pol_grp_1 + +# CREATE FABRIC POD SELECTOR TYPE ALL +- name: Create Fabric Pod Selector without blocks (check mode) + cisco.aci.aci_fabric_pod_selector: &fabric_pod_sel_without_blocks + <<: *aci_info + name: ansible_fabric_pod_pol_sel_1 + type: all + pod_profile: default + description: Created Ansible Pod Selector + check_mode: true + register: cm_create_fabric_pod_sel_without_blocks + +- name: Create Fabric Pod Selector without blocks + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_without_blocks + register: nm_create_fabric_pod_sel_without_blocks + +- name: Create Fabric Pod Selector without blocks again + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_without_blocks + register: nm_create_fabric_pod_sel_without_blocks_again + +- name: Verify create of Pod Selector + ansible.builtin.assert: + that: + - cm_create_fabric_pod_sel_without_blocks is changed + - cm_create_fabric_pod_sel_without_blocks.current == [] + - cm_create_fabric_pod_sel_without_blocks.previous == [] + - cm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.annotation == "orchestrator:ansible" + - cm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - cm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_1-typ-ALL" + - cm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_1" + - cm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.type == "ALL" + - nm_create_fabric_pod_sel_without_blocks is changed + - nm_create_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_create_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_create_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_1-typ-ALL" + - nm_create_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_1" + - nm_create_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.type == "ALL" + - nm_create_fabric_pod_sel_without_blocks.previous == [] + - nm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_1-typ-ALL" + - nm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_1" + - nm_create_fabric_pod_sel_without_blocks.proposed.fabricPodS.attributes.type == "ALL" + - nm_create_fabric_pod_sel_without_blocks_again is not changed + - nm_create_fabric_pod_sel_without_blocks_again.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_create_fabric_pod_sel_without_blocks_again.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_create_fabric_pod_sel_without_blocks_again.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_1-typ-ALL" + - nm_create_fabric_pod_sel_without_blocks_again.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_1" + - nm_create_fabric_pod_sel_without_blocks_again.current.0.fabricPodS.attributes.type == "ALL" + - nm_create_fabric_pod_sel_without_blocks_again.previous.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_create_fabric_pod_sel_without_blocks_again.previous.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_create_fabric_pod_sel_without_blocks_again.previous.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_1-typ-ALL" + - nm_create_fabric_pod_sel_without_blocks_again.previous.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_1" + - nm_create_fabric_pod_sel_without_blocks_again.previous.0.fabricPodS.attributes.type == "ALL" + +# REMOVE FABRIC POD SELECTOR +- name: Remove Fabric Pod Selector without blocks (check mode) + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_without_blocks + state: absent + check_mode: true + register: cm_remove_fabric_pod_sel_without_blocks + +- name: Remove Fabric Pod Selector without blocks + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_without_blocks + state: absent + register: nm_remove_fabric_pod_sel_without_blocks + +- name: Remove Fabric Pod Selector without blocks again + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_without_blocks + state: absent + register: nm_remove_fabric_pod_sel_without_blocks_again + +- name: Verify removal of Pod Selector + ansible.builtin.assert: + that: + - cm_remove_fabric_pod_sel_without_blocks is changed + - cm_remove_fabric_pod_sel_without_blocks.proposed == {} + - cm_remove_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - cm_remove_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - cm_remove_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_1-typ-ALL" + - cm_remove_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_1" + - cm_remove_fabric_pod_sel_without_blocks.current.0.fabricPodS.attributes.type == "ALL" + - cm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - cm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - cm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_1-typ-ALL" + - cm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_1" + - cm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.type == "ALL" + - nm_remove_fabric_pod_sel_without_blocks is changed + - nm_remove_fabric_pod_sel_without_blocks.proposed == {} + - nm_remove_fabric_pod_sel_without_blocks.current == [] + - nm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_1-typ-ALL" + - nm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_1" + - nm_remove_fabric_pod_sel_without_blocks.previous.0.fabricPodS.attributes.type == "ALL" + - nm_remove_fabric_pod_sel_without_blocks_again is not changed + - nm_remove_fabric_pod_sel_without_blocks_again.current == [] + - nm_remove_fabric_pod_sel_without_blocks_again.previous == [] + +# CREATE & UPDATE FABRIC POD SELECTOR TYPE RANGE +- name: Create Fabric Pod Selector with blocks and policy + cisco.aci.aci_fabric_pod_selector: &fabric_pod_sel_blocks + <<: *fabric_pod_sel_without_blocks + name: ansible_fabric_pod_pol_sel_2 + type: range + blocks: "2" + policy_group: ansible_fabric_pod_pol_grp + register: nm_update_fabric_pod_sel_with_blocks + +- name: Create Fabric Pod Selector with blocks and policy again + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_blocks + blocks: "2" + register: nm_update_fabric_pod_sel_with_blocks_again + +- name: Update Fabric Pod Selector with blocks and policy again with different notation + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_blocks + blocks: "2-2" + register: nm_update_fabric_pod_sel_with_blocks_again_different_notation + +- name: Update Fabric Pod Selector with changed blocks range + cisco.aci.aci_fabric_pod_selector: &fabric_pod_sel_blocks_range + <<: *fabric_pod_sel_blocks + blocks: "2-3,5,8-11" + register: nm_update_fabric_pod_sel_with_blocks_range + +- name: Update Fabric Pod Selector with removal of policy group + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_blocks_range + policy_group: "" + register: nm_update_fabric_pod_sel_with_blocks_no_policy + +- name: Verify update of Pod Selector + ansible.builtin.assert: + that: + - nm_update_fabric_pod_sel_with_blocks is changed + - nm_update_fabric_pod_sel_with_blocks.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks.current.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks.current.0.fabricPodS.children.0.fabricRsPodPGrp.attributes.tDn == "uni/fabric/funcprof/podpgrp-ansible_fabric_pod_pol_grp" + - nm_update_fabric_pod_sel_with_blocks.current.0.fabricPodS.children.1.fabricPodBlk.attributes.from_ == "2" + - nm_update_fabric_pod_sel_with_blocks.current.0.fabricPodS.children.1.fabricPodBlk.attributes.to_ == "2" + - nm_update_fabric_pod_sel_with_blocks.previous == [] + - nm_update_fabric_pod_sel_with_blocks_again is not changed + - nm_update_fabric_pod_sel_with_blocks_again.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks_again.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks_again.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks_again.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks_again.current.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks_again.current.0.fabricPodS.children.0.fabricRsPodPGrp.attributes.tDn == "uni/fabric/funcprof/podpgrp-ansible_fabric_pod_pol_grp" + - nm_update_fabric_pod_sel_with_blocks_again.current.0.fabricPodS.children.1.fabricPodBlk.attributes.from_ == "2" + - nm_update_fabric_pod_sel_with_blocks_again.current.0.fabricPodS.children.1.fabricPodBlk.attributes.to_ == "2" + - nm_update_fabric_pod_sel_with_blocks_again.previous.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks_again.previous.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks_again.previous.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks_again.previous.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks_again.previous.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks_again.previous.0.fabricPodS.children.0.fabricRsPodPGrp.attributes.tDn == "uni/fabric/funcprof/podpgrp-ansible_fabric_pod_pol_grp" + - nm_update_fabric_pod_sel_with_blocks_again.previous.0.fabricPodS.children.1.fabricPodBlk.attributes.from_ == "2" + - nm_update_fabric_pod_sel_with_blocks_again.previous.0.fabricPodS.children.1.fabricPodBlk.attributes.to_ == "2" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation is not changed + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.current.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.current.0.fabricPodS.children.0.fabricRsPodPGrp.attributes.tDn == "uni/fabric/funcprof/podpgrp-ansible_fabric_pod_pol_grp" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.current.0.fabricPodS.children.1.fabricPodBlk.attributes.from_ == "2" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.current.0.fabricPodS.children.1.fabricPodBlk.attributes.to_ == "2" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.previous.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.previous.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.previous.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.previous.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.previous.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.previous.0.fabricPodS.children.0.fabricRsPodPGrp.attributes.tDn == "uni/fabric/funcprof/podpgrp-ansible_fabric_pod_pol_grp" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.previous.0.fabricPodS.children.1.fabricPodBlk.attributes.from_ == "2" + - nm_update_fabric_pod_sel_with_blocks_again_different_notation.previous.0.fabricPodS.children.1.fabricPodBlk.attributes.to_ == "2" + - nm_update_fabric_pod_sel_with_blocks_range is changed + - nm_update_fabric_pod_sel_with_blocks_range.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks_range.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks_range.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks_range.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks_range.current.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks_range.current.0.fabricPodS.children.0.fabricRsPodPGrp.attributes.tDn == "uni/fabric/funcprof/podpgrp-ansible_fabric_pod_pol_grp" + - nm_update_fabric_pod_sel_with_blocks_range.current.0.fabricPodS.children | length == 4 + - nm_update_fabric_pod_sel_with_blocks_range.previous.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks_range.previous.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks_range.previous.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks_range.previous.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks_range.previous.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks_range.previous.0.fabricPodS.children.0.fabricRsPodPGrp.attributes.tDn == "uni/fabric/funcprof/podpgrp-ansible_fabric_pod_pol_grp" + - nm_update_fabric_pod_sel_with_blocks_range.previous.0.fabricPodS.children.1.fabricPodBlk.attributes.from_ == "2" + - nm_update_fabric_pod_sel_with_blocks_range.previous.0.fabricPodS.children.1.fabricPodBlk.attributes.to_ == "2" + - nm_update_fabric_pod_sel_with_blocks_no_policy is changed + - nm_update_fabric_pod_sel_with_blocks_no_policy.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks_no_policy.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks_no_policy.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks_no_policy.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks_no_policy.current.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks_no_policy.current.0.fabricPodS.children | length == 3 + - nm_update_fabric_pod_sel_with_blocks_no_policy.previous.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - nm_update_fabric_pod_sel_with_blocks_no_policy.previous.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - nm_update_fabric_pod_sel_with_blocks_no_policy.previous.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - nm_update_fabric_pod_sel_with_blocks_no_policy.previous.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - nm_update_fabric_pod_sel_with_blocks_no_policy.previous.0.fabricPodS.attributes.type == "range" + - nm_update_fabric_pod_sel_with_blocks_no_policy.previous.0.fabricPodS.children.0.fabricRsPodPGrp.attributes.tDn == "uni/fabric/funcprof/podpgrp-ansible_fabric_pod_pol_grp" + - nm_update_fabric_pod_sel_with_blocks_no_policy.previous.0.fabricPodS.children | length == 4 + +# QUERY FABRIC POD SELECTOR +- name: Create another Fabric Pod Selector + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_blocks + name: ansible_fabric_pod_pol_sel_3 + blocks: "6" + +- name: Query a Fabric Pod Selector + cisco.aci.aci_fabric_pod_selector: + <<: *fabric_pod_sel_blocks + state: query + register: query_one + +- name: Query all Fabric Pod Selectors + cisco.aci.aci_fabric_pod_selector: + <<: *aci_info + state: query + register: query_all + +- name: Verify queries of Fabric Pod Selectors + ansible.builtin.assert: + that: + - query_one is not changed + - query_one.current | length == 1 + - query_one.current.0.fabricPodS.attributes.annotation == "orchestrator:ansible" + - query_one.current.0.fabricPodS.attributes.descr == "Created Ansible Pod Selector" + - query_one.current.0.fabricPodS.attributes.dn == "uni/fabric/podprof-default/pods-ansible_fabric_pod_pol_sel_2-typ-range" + - query_one.current.0.fabricPodS.attributes.name == "ansible_fabric_pod_pol_sel_2" + - query_one.current.0.fabricPodS.attributes.type == "range" + - query_one.current.0.fabricPodS.children | length == 3 + - query_all is not changed + - query_all.current | length >= 2 + +# ERRORS +- name: Create Fabric Pod Selector with type range and without blocks (error) + cisco.aci.aci_fabric_pod_selector: + <<: *aci_info + name: ansible_fabric_pod_pol_sel_2 + type: range + pod_profile: default + register: err_range_without_blocks + ignore_errors: true + +- name: Verify errors of Fabric Pod Selectors + ansible.builtin.assert: + that: + - err_range_without_blocks is not changed + - err_range_without_blocks is failed + - err_range_without_blocks.msg == "The 'blocks' parameter is required when the 'type' parameter is set to 'range' and 'state' parameter is set to 'present'."