From 9a6e00d19a9a73254c73e349a4901ea57687692f Mon Sep 17 00:00:00 2001 From: Sabari Jaganathan Date: Thu, 13 Jan 2022 07:55:55 +0530 Subject: [PATCH 1/3] Added aaa_domain, aaa_role and custom_privilege modules and test files --- plugins/modules/aci_aaa_custom_privilege.py | 363 +++++++++++++++++ plugins/modules/aci_aaa_domain.py | 279 +++++++++++++ plugins/modules/aci_aaa_role.py | 384 ++++++++++++++++++ .../targets/aci_aaa_custom_privilege/aliases | 2 + .../aci_aaa_custom_privilege/tasks/main.yml | 222 ++++++++++ .../targets/aci_aaa_domain/aliases | 2 + .../targets/aci_aaa_domain/tasks/main.yml | 192 +++++++++ .../integration/targets/aci_aaa_role/aliases | 2 + .../targets/aci_aaa_role/tasks/main.yml | 193 +++++++++ 9 files changed, 1639 insertions(+) create mode 100644 plugins/modules/aci_aaa_custom_privilege.py create mode 100644 plugins/modules/aci_aaa_domain.py create mode 100644 plugins/modules/aci_aaa_role.py create mode 100644 tests/integration/targets/aci_aaa_custom_privilege/aliases create mode 100644 tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml create mode 100644 tests/integration/targets/aci_aaa_domain/aliases create mode 100644 tests/integration/targets/aci_aaa_domain/tasks/main.yml create mode 100644 tests/integration/targets/aci_aaa_role/aliases create mode 100644 tests/integration/targets/aci_aaa_role/tasks/main.yml diff --git a/plugins/modules/aci_aaa_custom_privilege.py b/plugins/modules/aci_aaa_custom_privilege.py new file mode 100644 index 000000000..dd5fc3bb4 --- /dev/null +++ b/plugins/modules/aci_aaa_custom_privilege.py @@ -0,0 +1,363 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) +# 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_aaa_custom_privilege +short_description: Manage AAA RBAC Node Rules (aaa:RbacClassPriv) +description: +- Manage AAA Custom Privileges with RBAC Rules on Cisco ACI fabrics. +options: + name: + description: + - Name of the object class for which you are configuring access. + type: str + aliases: [ custom_privilege_name ] + wPriv: + description: + - Name of the custom privilege that will include write access to objects of the class. + type: str + aliases: [ write_priv ] + choices: [ + custom-privilege-1, + custom-privilege-2, + custom-privilege-3, + custom-privilege-4, + custom-privilege-5, + custom-privilege-6, + custom-privilege-7, + custom-privilege-8, + custom-privilege-9, + custom-privilege-10, + custom-privilege-11, + custom-privilege-12, + custom-privilege-13, + custom-privilege-14, + custom-privilege-15, + custom-privilege-16, + custom-privilege-17, + custom-privilege-18, + custom-privilege-19, + custom-privilege-20, + custom-privilege-21, + custom-privilege-22 + ] + rPriv: + description: + - Name of the custom privilege that will include read access to objects of the class. + type: str + aliases: [ read_priv ] + choices: [ + custom-privilege-1, + custom-privilege-2, + custom-privilege-3, + custom-privilege-4, + custom-privilege-5, + custom-privilege-6, + custom-privilege-7, + custom-privilege-8, + custom-privilege-9, + custom-privilege-10, + custom-privilege-11, + custom-privilege-12, + custom-privilege-13, + custom-privilege-14, + custom-privilege-15, + custom-privilege-16, + custom-privilege-17, + custom-privilege-18, + custom-privilege-19, + custom-privilege-20, + custom-privilege-21, + custom-privilege-22 + ] + 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 + +seealso: +- module: cisco.aci.aci_aaa_domain +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(aaa:Domain). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Sabari Jaganathan (@sajagana) +""" + +EXAMPLES = r""" +- name: Add a custom privilege + cisco.aci.aci_aaa_custom_privilege: + host: apic + username: admin + password: SomeSecretPassword + name: fabricPod + wPriv: custom-privilege-1 + rPriv: custom-privilege-1 + state: present + delegate_to: localhost + +- name: Add list of custom privileges + cisco.aci.aci_aaa_custom_privilege: + host: apic + username: admin + password: SomeSecretPassword + name: "{{ item.name }}" + wPriv: "{{ item.wPriv }}" + rPriv: "{{ item.rPriv | default('') }}" + state: present + with_items: + - name: fvTenant + wPriv: custom-privilege-2 + rPriv: custom-privilege-2 + - name: aaaUser + wPriv: custom-privilege-3 + delegate_to: localhost + +- name: Query a custom privilege with name + cisco.aci.aci_aaa_custom_privilege: + host: apic + username: admin + password: SomeSecretPassword + name: fabricPod + state: query + delegate_to: localhost + +- name: Query all custom privileges + cisco.aci.aci_aaa_custom_privilege: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Remove a custom privilege + cisco.aci.aci_aaa_custom_privilege: + host: apic + username: admin + password: SomeSecretPassword + name: fabricPod + 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 + +CUSTOM_PRIVILEGES = [ + "custom-privilege-1", + "custom-privilege-2", + "custom-privilege-3", + "custom-privilege-4", + "custom-privilege-5", + "custom-privilege-6", + "custom-privilege-7", + "custom-privilege-8", + "custom-privilege-9", + "custom-privilege-10", + "custom-privilege-11", + "custom-privilege-12", + "custom-privilege-13", + "custom-privilege-14", + "custom-privilege-15", + "custom-privilege-16", + "custom-privilege-17", + "custom-privilege-18", + "custom-privilege-19", + "custom-privilege-20", + "custom-privilege-21", + "custom-privilege-22", +] + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type="str", aliases=["custom_privilege_name"]), + wPriv=dict( + type="str", + aliases=["write_priv"], + choices=CUSTOM_PRIVILEGES, + ), + rPriv=dict( + type="str", + aliases=["read_priv"], + choices=CUSTOM_PRIVILEGES, + ), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + name_alias=dict(type="str"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["name"]], + ["state", "present", ["name"]], + ], + ) + + aci = ACIModule(module) + + name = module.params.get("name") + wPriv = module.params.get("wPriv") + rPriv = module.params.get("rPriv") + state = module.params.get("state") + name_alias = module.params.get("name_alias") + + aci.construct_url( + root_class=dict( + aci_class="aaaRbacClassPriv", + aci_rn="rbacdb/rbacclpriv-{0}".format(name), + module_object=name, + target_filter={"name": name}, + ), + ) + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="aaaRbacClassPriv", + class_config=dict( + name=name, + wPriv=wPriv, + rPriv=rPriv, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class="aaaRbacClassPriv") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/aci_aaa_domain.py b/plugins/modules/aci_aaa_domain.py new file mode 100644 index 000000000..bd6147d67 --- /dev/null +++ b/plugins/modules/aci_aaa_domain.py @@ -0,0 +1,279 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) +# 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_aaa_domain +short_description: Manage AAA domains (aaa:Domain) +description: +- Manage AAA domains on Cisco ACI fabrics. +options: + name: + description: + - The name of the aaa domain. + type: str + aliases: [ aaa_domain ] + description: + description: + - Description of the aaa domain. + type: str + aliases: [ descr ] + restricted_rbac_domain: + description: + - C(True) to enable Restricted RBAC Domain on the aaa security domain. + type: bool + choices: [ false, true ] + 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 + +seealso: +- module: cisco.aci.aci_aaa_role +- name: Manage AAA roles (aaa:Role) + description: More information about the AAA roles class B(aaa:Role). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Sabari Jaganathan (@sajagana) +""" + +EXAMPLES = r""" +- name: Add an aaa security domain + cisco.aci.aci_aaa_domain: + host: apic + username: admin + password: SomeSecretPassword + name: anstest_security_domain + description: "Anstest Sec Domain Descr" + state: present + delegate_to: localhost + +- name: Add list of aaa security domain + cisco.aci.aci_aaa_domain: + host: apic + username: admin + password: SomeSecretPassword + name: "{{ item.name }}" + description: "{{ item.description }}" + state: present + with_items: + - name: anstest1 + description: "Anstest Sec Domain Descr 1" + - name: anstest2 + description: "Anstest Sec Domain Descr 2" + delegate_to: localhost + +- name: Query an aaa security domain with name + cisco.aci.aci_aaa_domain: + host: apic + username: admin + password: SomeSecretPassword + name: anstest_security_domain + state: query + delegate_to: localhost + +- name: Query all aaa security domains + cisco.aci.aci_aaa_domain: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Remove an aaa security domain + cisco.aci.aci_aaa_domain: + host: apic + username: admin + password: SomeSecretPassword + name: anstest_security_domain + 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 + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type="str", aliases=["aaa_domain"]), + description=dict(type="str", aliases=["descr"]), + restricted_rbac_domain=dict(type="bool", choices=[False, True]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + name_alias=dict(type="str"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["name"]], + ["state", "present", ["name"]], + ], + ) + + name = module.params.get("name") + description = module.params.get("description") + restricted_rbac_domain = module.params.get("restricted_rbac_domain") + state = module.params.get("state") + name_alias = module.params.get("name_alias") + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class="aaaDomain", + aci_rn="userext/domain-{0}".format(name), + module_object=name, + target_filter={"name": name}, + ), + ) + aci.get_existing() + restricted_rbac_domain_mapping = {False: "no", True: "yes"} + restricted_rbac_domain_state = restricted_rbac_domain_mapping.get(restricted_rbac_domain) + if state == "present": + aci.payload( + aci_class="aaaDomain", + class_config=dict( + name=name, + descr=description, + restrictedRbacDomain=restricted_rbac_domain_state, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class="aaaDomain") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/aci_aaa_role.py b/plugins/modules/aci_aaa_role.py new file mode 100644 index 000000000..d2da8aa78 --- /dev/null +++ b/plugins/modules/aci_aaa_role.py @@ -0,0 +1,384 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) +# 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_aaa_role +short_description: Manage AAA roles (aaa:Role) +description: +- Manage AAA roles on Cisco ACI fabrics. +options: + name: + description: + - The name of the aaa role. + type: str + aliases: [ aaa_role ] + priv: + description: + - The privilege(s) assigned to a role. + - Use Comma separated values to assign multiple privileges to a Role. + type: list + elements: str + choices: [ + admin, + aaa, + tenant-connectivity, + tenant-protocol, + vmm-policy, + tenant-ext-connectivity, + tenant-ext-protocol, + tenant-qos, + tenant-security, + tenant-network-profile, + tenant-epg, + fabric-connectivity, + fabric-protocol, + fabric-equipment, + access-connectivity, + access-protocol, + access-equipment, + access-qos, + nw-svc-params, + ops, + nw-svc-policy, + site-admin, + site-policy, + config-manager, + custom-privilege-1, + custom-privilege-2, + custom-privilege-3, + custom-privilege-4, + custom-privilege-5, + custom-privilege-6, + custom-privilege-7, + custom-privilege-8, + custom-privilege-9, + custom-privilege-10, + custom-privilege-11, + custom-privilege-12, + custom-privilege-13, + custom-privilege-14, + custom-privilege-15, + custom-privilege-16, + custom-privilege-17, + custom-privilege-18, + custom-privilege-19, + custom-privilege-20, + custom-privilege-21, + custom-privilege-22, + custom-port-privilege + ] + description: + description: + - Description of the aaa role. + type: str + aliases: [ descr ] + 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 + +seealso: +- module: cisco.aci.aci_aaa_domain +- name: Manage AAA domains (aaa:Domain) + description: More information about the AAA domains class B(aaa:Domain). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Sabari Jaganathan (@sajagana) +""" + + +EXAMPLES = r""" +- name: Add a aaa role + cisco.aci.aci_aaa_role: + host: apic + username: admin + password: SomeSecretPassword + name: anstest + priv: aaa + state: present + delegate_to: localhost + +- name: Add list of aaa roles + cisco.aci.aci_aaa_role: + host: apic + username: admin + password: SomeSecretPassword + name: "{{ item.name }}" + priv: "{{ item.priv }}" + state: present + delegate_to: localhost + with_items: + - name: anstest1 + priv: site-admin + - name: anstest2 + priv: site-policy + +- name: Query a aaa role with name + cisco.aci.aci_aaa_role: + host: apic + username: admin + password: SomeSecretPassword + name: anstest + state: query + delegate_to: localhost + +- name: Query all aaa roles + cisco.aci.aci_aaa_role: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Remove a aaa role with name + cisco.aci.aci_aaa_role: + host: apic + username: admin + password: SomeSecretPassword + name: anstest + 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 + +PRIVILEGES = [ + "admin", + "aaa", + "tenant-connectivity", + "tenant-protocol", + "vmm-policy", + "tenant-ext-connectivity", + "tenant-ext-protocol", + "tenant-qos", + "tenant-security", + "tenant-network-profile", + "tenant-epg", + "fabric-connectivity", + "fabric-protocol", + "fabric-equipment", + "access-connectivity", + "access-protocol", + "access-equipment", + "access-qos", + "nw-svc-params", + "ops", + "nw-svc-policy", + "site-admin", + "site-policy", + "config-manager", + "custom-privilege-1", + "custom-privilege-2", + "custom-privilege-3", + "custom-privilege-4", + "custom-privilege-5", + "custom-privilege-6", + "custom-privilege-7", + "custom-privilege-8", + "custom-privilege-9", + "custom-privilege-10", + "custom-privilege-11", + "custom-privilege-12", + "custom-privilege-13", + "custom-privilege-14", + "custom-privilege-15", + "custom-privilege-16", + "custom-privilege-17", + "custom-privilege-18", + "custom-privilege-19", + "custom-privilege-20", + "custom-privilege-21", + "custom-privilege-22", + "custom-port-privilege", +] + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type="str", aliases=["aaa_role"]), + priv=dict(type="list", elements="str", choices=PRIVILEGES), + description=dict(type="str", aliases=["descr"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + name_alias=dict(type="str"), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["name"]], + ["state", "present", ["name", "priv"]], + ], + ) + + name = module.params.get("name") + description = module.params.get("description") + priv = module.params.get("priv") + state = module.params.get("state") + name_alias = module.params.get("name_alias") + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class="aaaRole", + aci_rn="userext/role-{0}".format(name), + module_object=name, + target_filter={"name": name}, + ), + ) + aci.get_existing() + + if isinstance(priv, list): + formatted_privileges = ",".join(priv) + else: + formatted_privileges = priv + + if state == "present": + aci.payload( + aci_class="aaaRole", + class_config=dict( + name=name, + priv=formatted_privileges, + descr=description, + nameAlias=name_alias, + ), + ) + + aci.get_diff(aci_class="aaaRole") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/aci_aaa_custom_privilege/aliases b/tests/integration/targets/aci_aaa_custom_privilege/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_aaa_custom_privilege/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml b/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml new file mode 100644 index 000000000..fe9d40d0f --- /dev/null +++ b/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml @@ -0,0 +1,222 @@ +# Test code for the ACI modules +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) + +# 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 + +# SET VARS +- 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: Ensure fabricPod custom privilege does not exists + cisco.aci.aci_aaa_custom_privilege: &fabric_pod_absent + <<: *aci_info + name: fabricPod + state: absent + +- name: Add a custom privilege with check mode + cisco.aci.aci_aaa_custom_privilege: &cm_cp_present + <<: *fabric_pod_absent + wPriv: custom-privilege-1 + rPriv: custom-privilege-1 + state: present + check_mode: yes + register: cm_cp_present + +- name: Assertions check for add a custom privilege with check mode + assert: + that: + - cm_cp_present is changed + - cm_cp_present.current | length == 0 + - cm_cp_present.previous | length == 0 + - cm_cp_present.sent.aaaRbacClassPriv.attributes.name == 'fabricPod' + - cm_cp_present.sent.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - cm_cp_present.sent.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + +- name: Add a custom privilege with normal mode + cisco.aci.aci_aaa_custom_privilege: &nm_cp_present + <<: *cm_cp_present + register: nm_cp_present + +- name: Assertions check for add a custom privilege with normal mode + assert: + that: + - nm_cp_present is changed + - nm_cp_present.current | length == 1 + - nm_cp_present.previous | length == 0 + - nm_cp_present.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - nm_cp_present.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - nm_cp_present.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + +- name: Add a custom privilege with normal mode - idempotency works + cisco.aci.aci_aaa_custom_privilege: + <<: *nm_cp_present + register: idempotency_cp_present + +- name: Idempotency assertions check for add a custom privilege with normal mode + assert: + that: + - idempotency_cp_present is not changed + - idempotency_cp_present.current | length == 1 + - idempotency_cp_present.previous | length == 1 + - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + +- name: Ensure fvTenant custom privilege does not exists + cisco.aci.aci_aaa_custom_privilege: &fv_tenant_absent + <<: *aci_info + name: fvTenant + state: absent + +- name: Add a custom privilege with none privileges + cisco.aci.aci_aaa_custom_privilege: &fv_tenant_present + <<: *fv_tenant_absent + state: present + register: cp_with_none_priv + +- name: Assertions check for add a custom privilege with none privileges + assert: + that: + - cp_with_none_priv is changed + - cp_with_none_priv.current | length == 1 + - cp_with_none_priv.previous | length == 0 + - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.wPriv == '' + - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.rPriv == '' + +- name: Update fv_tenant_present custom privilege with 'custom-privilege-2' privilege + cisco.aci.aci_aaa_custom_privilege: + <<: *fv_tenant_present + wPriv: custom-privilege-2 + rPriv: custom-privilege-2 + register: update_cp_with_priv + +- name: Assertions check for update fv_tenant_present custom privilege with 'custom-privilege-2' privilege + assert: + that: + - update_cp_with_priv is changed + - update_cp_with_priv.current | length == 1 + - update_cp_with_priv.previous | length == 1 + - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-2' + - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' + - update_cp_with_priv.previous.0.aaaRbacClassPriv.attributes.wPriv == '' + - update_cp_with_priv.previous.0.aaaRbacClassPriv.attributes.rPriv == '' + +- name: Update fv_tenant_present - wPriv to 'custom-privilege-3' privilege + cisco.aci.aci_aaa_custom_privilege: + <<: *fv_tenant_present + wPriv: custom-privilege-3 + register: update_cp_wPriv_check + +- name: Assertions check for update fv_tenant_present - wPriv to 'custom-privilege-3' privilege + assert: + that: + - update_cp_wPriv_check is changed + - update_cp_wPriv_check.current | length == 1 + - update_cp_wPriv_check.previous | length == 1 + - update_cp_wPriv_check.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - update_cp_wPriv_check.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-3' + - update_cp_wPriv_check.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' + - update_cp_wPriv_check.previous.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - update_cp_wPriv_check.previous.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-2' + - update_cp_wPriv_check.previous.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' + +- name: Query a custom privilege with name + cisco.aci.aci_aaa_custom_privilege: + <<: *aci_info + name: fabricPod + state: query + register: query_a_cp_with_name + +- name: Assertions check for query a custom privilege with name + assert: + that: + - query_a_cp_with_name is not changed + - query_a_cp_with_name.current | length == 1 + - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + +- name: Query all custom privileges + cisco.aci.aci_aaa_custom_privilege: + <<: *aci_info + state: query + register: query_all_cp + +- name: Assertions check for query all custom privileges + assert: + that: + - query_all_cp is not changed + - query_all_cp.current | length >= 2 + +- name: Remove a custom privilege with check mode + cisco.aci.aci_aaa_custom_privilege: &cm_cp_absent + <<: *aci_info + name: fabricPod + state: absent + check_mode: yes + register: cm_cp_absent + +- name: Assertions check for remove a custom privilege with check mode + assert: + that: + - cm_cp_absent is changed + - cm_cp_absent.current | length == 1 + - cm_cp_absent.previous | length == 1 + - cm_cp_absent.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - cm_cp_absent.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + +- name: Remove a custom privilege with normal mode + cisco.aci.aci_aaa_custom_privilege: &nm_cp_absent + <<: *cm_cp_absent + register: nm_cp_absent + +- name: Assertions check for remove a custom privilege with normal mode + assert: + that: + - nm_cp_absent is changed + - nm_cp_absent.current == [] + - nm_cp_absent.previous | length == 1 + - nm_cp_absent.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + +- name: Remove a custom privilege with normal mode - idempotency works + cisco.aci.aci_aaa_custom_privilege: + <<: *nm_cp_absent + register: idempotency_cp_absent + +- name: Idempotency assertions check for remove a custom privilege with normal mode + assert: + that: + - idempotency_cp_absent is not changed + - idempotency_cp_absent.current == [] + - idempotency_cp_absent.previous == [] + +- name: Query a Removed custom privilege with name + cisco.aci.aci_aaa_custom_privilege: + <<: *aci_info + name: fabricPod + state: query + register: removed_cp_query_result + +- name: Assertions check for Removed custom privilege with name + assert: + that: + - removed_cp_query_result is not changed + - removed_cp_query_result.current == [] diff --git a/tests/integration/targets/aci_aaa_domain/aliases b/tests/integration/targets/aci_aaa_domain/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_aaa_domain/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_aaa_domain/tasks/main.yml b/tests/integration/targets/aci_aaa_domain/tasks/main.yml new file mode 100644 index 000000000..3fa6ecdb5 --- /dev/null +++ b/tests/integration/targets/aci_aaa_domain/tasks/main.yml @@ -0,0 +1,192 @@ +# Test code for the ACI modules +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) + +# 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 + +# SET VARS +- 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: Ensure anstest_sec_domain does not exists + cisco.aci.aci_aaa_domain: &sec_domain_absent + <<: *aci_info + name: anstest_sec_domain + description: "Anstest Sec Domain Descr" + state: absent + +- name: Add anstest_sec_domain security domain with check mode + cisco.aci.aci_aaa_domain: &cm_sec_domain_present + <<: *sec_domain_absent + state: present + check_mode: yes + register: cm_sec_domain_present + +- name: Assertions check for add anstest_sec_domain security domain with check mode + assert: + that: + - cm_sec_domain_present is changed + - cm_sec_domain_present.current | length == 0 + - cm_sec_domain_present.previous | length == 0 + - cm_sec_domain_present.sent.aaaDomain.attributes.name == 'anstest_sec_domain' + +- name: Add anstest_sec_domain security domain with normal mode + cisco.aci.aci_aaa_domain: &nm_sec_domain_present + <<: *cm_sec_domain_present + register: nm_sec_domain_present + +- name: Assertions check for add anstest_sec_domain security domain with normal mode + assert: + that: + - nm_sec_domain_present is changed + - nm_sec_domain_present.current | length == 1 + - nm_sec_domain_present.previous | length == 0 + - nm_sec_domain_present.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - nm_sec_domain_present.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + +- name: Add anstest_sec_domain security domain with normal mode - idempotency works + cisco.aci.aci_aaa_domain: + <<: *nm_sec_domain_present + register: idempotency_sec_domain_present + +- name: Idempotency assertions check for add anstest_sec_domain security domain with normal mode + assert: + that: + - idempotency_sec_domain_present is not changed + - idempotency_sec_domain_present.current | length == 1 + - idempotency_sec_domain_present.previous | length == 1 + - idempotency_sec_domain_present.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - idempotency_sec_domain_present.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + - idempotency_sec_domain_present.previous.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - idempotency_sec_domain_present.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + +- name: Update anstest_sec_domain security domain restricted_rbac_domain state to true + cisco.aci.aci_aaa_domain: + <<: *nm_sec_domain_present + restricted_rbac_domain: true + register: update_sec_domain_present_true + +- name: Assertions check for update anstest_sec_domain security domain restricted_rbac_domain state to true + assert: + that: + - update_sec_domain_present_true is changed + - update_sec_domain_present_true.current | length == 1 + - update_sec_domain_present_true.previous | length == 1 + - update_sec_domain_present_true.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - update_sec_domain_present_true.current.0.aaaDomain.attributes.restrictedRbacDomain == 'yes' + - update_sec_domain_present_true.sent.aaaDomain.attributes.restrictedRbacDomain == 'yes' + - update_sec_domain_present_true.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + +- name: Update anstest_sec_domain security domain restricted_rbac_domain state to false + cisco.aci.aci_aaa_domain: + <<: *nm_sec_domain_present + restricted_rbac_domain: false + register: update_sec_domain_present_false + +- name: Assertions check for update anstest_sec_domain security domain restricted_rbac_domain state to false + assert: + that: + - update_sec_domain_present_false is changed + - update_sec_domain_present_false.current | length == 1 + - update_sec_domain_present_false.previous | length == 1 + - update_sec_domain_present_false.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - update_sec_domain_present_false.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + - update_sec_domain_present_false.sent.aaaDomain.attributes.restrictedRbacDomain == 'no' + - update_sec_domain_present_false.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'yes' + +- name: Query a security domain with name + cisco.aci.aci_aaa_domain: + <<: *aci_info + name: anstest_sec_domain + state: query + register: query_sec_domain_with_name + +- name: Assertions check for query a security domain with name + assert: + that: + - query_sec_domain_with_name is not changed + - query_sec_domain_with_name.current | length == 1 + - query_sec_domain_with_name.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - query_sec_domain_with_name.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + - query_sec_domain_with_name.current.0.aaaDomain.attributes.descr == 'Anstest Sec Domain Descr' + +- name: Query all security domains + cisco.aci.aci_aaa_domain: + <<: *aci_info + state: query + register: query_all_sec_domains + +- name: Assertions check for query all security domains + assert: + that: + - query_all_sec_domains is not changed + - query_all_sec_domains.current | length >= 1 + +- name: Remove anstest_sec_domain security domain with check mode + cisco.aci.aci_aaa_domain: &cm_sec_domain_absent + <<: *nm_sec_domain_present + state: absent + check_mode: yes + register: cm_sec_domain_absent + +- name: Assertions check for remove anstest_sec_domain security domain with check mode + assert: + that: + - cm_sec_domain_absent is changed + - cm_sec_domain_absent.current | length == 1 + - cm_sec_domain_absent.previous | length == 1 + - cm_sec_domain_absent.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - cm_sec_domain_absent.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + - cm_sec_domain_absent.previous.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - cm_sec_domain_absent.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + +- name: Remove anstest_sec_domain security domain with normal mode + cisco.aci.aci_aaa_domain: &nm_sec_domain_absent + <<: *cm_sec_domain_absent + register: nm_sec_domain_absent + +- name: Assertions check for remove anstest_sec_domain security domain with normal mode + assert: + that: + - nm_sec_domain_absent is changed + - nm_sec_domain_absent.current | length == 0 + - nm_sec_domain_absent.previous | length == 1 + - nm_sec_domain_absent.previous.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - nm_sec_domain_absent.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + +- name: Remove anstest_sec_domain security domain with normal mode - idempotency works + cisco.aci.aci_aaa_domain: + <<: *nm_sec_domain_absent + register: idempotency_sec_domain_absent + +- name: Idempotency assertions check for remove anstest_sec_domain security domain with normal mode + assert: + that: + - idempotency_sec_domain_absent is not changed + - idempotency_sec_domain_absent.current | length == 0 + - idempotency_sec_domain_absent.previous | length == 0 + +- name: Query a removed security domain with name + cisco.aci.aci_aaa_domain: + <<: *aci_info + name: anstest_sec_domain + state: query + register: removed_sec_domain_with_name + +- name: Assertions check for query a removed security domain with name + assert: + that: + - removed_sec_domain_with_name is not changed + - removed_sec_domain_with_name.current | length == 0 diff --git a/tests/integration/targets/aci_aaa_role/aliases b/tests/integration/targets/aci_aaa_role/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_aaa_role/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_aaa_role/tasks/main.yml b/tests/integration/targets/aci_aaa_role/tasks/main.yml new file mode 100644 index 000000000..5c58e5f2e --- /dev/null +++ b/tests/integration/targets/aci_aaa_role/tasks/main.yml @@ -0,0 +1,193 @@ +# Test code for the ACI modules +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) + +# 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 + +# SET VARS +- 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: Ensure an anstest_role aaa role does not exists + cisco.aci.aci_aaa_role: &anstest_role_absent + <<: *aci_info + name: anstest_role + state: absent + +- name: Add an anstest_role aaa role with check mode + cisco.aci.aci_aaa_role: &cm_anstest_role_present + <<: *anstest_role_absent + priv: aaa + state: present + check_mode: yes + register: cm_anstest_role_present + +- name: Assertions check for add an anstest_role aaa role with check mode + assert: + that: + - cm_anstest_role_present is changed + - cm_anstest_role_present.current | length == 0 + - cm_anstest_role_present.previous | length == 0 + - cm_anstest_role_present.sent.aaaRole.attributes.name == 'anstest_role' + - cm_anstest_role_present.sent.aaaRole.attributes.priv == 'aaa' + +- name: Add an anstest_role aaa role with normal mode + cisco.aci.aci_aaa_role: &nm_anstest_role_present + <<: *cm_anstest_role_present + register: nm_anstest_role_present + +- name: Assertions check for add an anstest_role aaa role with normal mode + assert: + that: + - nm_anstest_role_present is changed + - nm_anstest_role_present.current | length == 1 + - nm_anstest_role_present.previous | length == 0 + - nm_anstest_role_present.current.0.aaaRole.attributes.name == 'anstest_role' + - nm_anstest_role_present.current.0.aaaRole.attributes.priv == 'aaa' + +- name: Add an anstest_role aaa role with normal mode - idempotency works + cisco.aci.aci_aaa_role: + <<: *nm_anstest_role_present + register: idempotency_anstest_role_present + +- name: Idempotency assertions check for add an anstest_role aaa role with normal mode + assert: + that: + - idempotency_anstest_role_present is not changed + - idempotency_anstest_role_present.current | length == 1 + - idempotency_anstest_role_present.previous | length == 1 + - idempotency_anstest_role_present.current.0.aaaRole.attributes.name == 'anstest_role' + - idempotency_anstest_role_present.previous.0.aaaRole.attributes.name == 'anstest_role' + - idempotency_anstest_role_present.current.0.aaaRole.attributes.priv == 'aaa' + - idempotency_anstest_role_present.previous.0.aaaRole.attributes.priv == 'aaa' + +- name: Update an anstest_role with list of privileges + cisco.aci.aci_aaa_role: + <<: *nm_anstest_role_present + priv: [ "admin", "aaa", "tenant-connectivity"] + register: anstest_role_with_list_priv + +- name: Assertions check for update an anstest_role with list of privileges + assert: + that: + - anstest_role_with_list_priv is changed + - anstest_role_with_list_priv.current | length == 1 + - anstest_role_with_list_priv.previous | length == 1 + - anstest_role_with_list_priv.current.0.aaaRole.attributes.name == 'anstest_role' + - anstest_role_with_list_priv.previous.0.aaaRole.attributes.name == 'anstest_role' + - anstest_role_with_list_priv.current.0.aaaRole.attributes.priv == 'aaa,admin,tenant-connectivity' + - anstest_role_with_list_priv.previous.0.aaaRole.attributes.priv == 'aaa' + +- name: Update an anstest_role with admin privilege + cisco.aci.aci_aaa_role: + <<: *nm_anstest_role_present + priv: "admin" + register: anstest_role_with_valid_priv + +- name: Assertions check for update an anstest_role with admin privilege + assert: + that: + - anstest_role_with_valid_priv is changed + - anstest_role_with_valid_priv.current | length == 1 + - anstest_role_with_valid_priv.previous | length == 1 + - anstest_role_with_valid_priv.current.0.aaaRole.attributes.name == 'anstest_role' + - anstest_role_with_valid_priv.previous.0.aaaRole.attributes.name == 'anstest_role' + - anstest_role_with_valid_priv.current.0.aaaRole.attributes.priv == 'admin' + - anstest_role_with_valid_priv.previous.0.aaaRole.attributes.priv == 'aaa,admin,tenant-connectivity' + +- name: Update an anstest_role with invalid list of privileges + cisco.aci.aci_aaa_role: + <<: *nm_anstest_role_present + priv: [ "admin", "aaa", "tenant-connectivity123"] + register: anstest_role_with_invalid_list_priv + ignore_errors: yes + +- name: Update an anstest_role with invalid privilege + cisco.aci.aci_aaa_role: + <<: *nm_anstest_role_present + priv: "admin123" + register: anstest_role_with_invalid_priv + ignore_errors: yes + +- name: Query an aaa role with name + cisco.aci.aci_aaa_role: + <<: *aci_info + name: anstest_role + state: query + register: anstest_role_query_result + +- name: Assertions check for query a aaa role with name + assert: + that: + - anstest_role_query_result is not changed + - anstest_role_query_result.current | length == 1 + - anstest_role_query_result.current.0.aaaRole.attributes.name == 'anstest_role' + - anstest_role_query_result.current.0.aaaRole.attributes.priv == 'admin' + +- name: Query all aaa roles + cisco.aci.aci_aaa_role: + <<: *aci_info + state: query + register: query_all_roles + +- name: Assertions check for query all aaa roles + assert: + that: + - query_all_roles is not changed + - query_all_roles.current | length >= 1 + +- name: Remove an anstest_role aaa role with check mode + cisco.aci.aci_aaa_role: &cm_anstest_role_absent + <<: *nm_anstest_role_present + state: absent + check_mode: yes + register: cm_anstest_role_absent + +- name: Assertions check for remove an anstest_role aaa role with check mode + assert: + that: + - cm_anstest_role_absent is changed + - cm_anstest_role_absent.current | length == 1 + - cm_anstest_role_absent.previous | length == 1 + - cm_anstest_role_absent.current.0.aaaRole.attributes.name == 'anstest_role' + - cm_anstest_role_absent.previous.0.aaaRole.attributes.name == 'anstest_role' + - cm_anstest_role_absent.current.0.aaaRole.attributes.priv == 'admin' + - cm_anstest_role_absent.previous.0.aaaRole.attributes.priv == 'admin' + +- name: Remove an anstest_role aaa role with normal mode + cisco.aci.aci_aaa_role: &nm_anstest_role_absent + <<: *cm_anstest_role_absent + register: nm_anstest_role_absent + +- name: Assertions check for remove an anstest_role aaa role with normal mode + assert: + that: + - nm_anstest_role_absent is changed + - nm_anstest_role_absent.current | length == 0 + - nm_anstest_role_absent.previous | length == 1 + - nm_anstest_role_absent.previous.0.aaaRole.attributes.name == 'anstest_role' + - nm_anstest_role_absent.previous.0.aaaRole.attributes.priv == 'admin' + +- name: Remove an anstest_role aaa role with normal mode - idempotency works + cisco.aci.aci_aaa_role: + <<: *nm_anstest_role_absent + register: idempotency_anstest_role_absent + +- name: Idempotency assertions check for remove an anstest_role aaa role with normal mode + assert: + that: + - idempotency_anstest_role_absent is not changed + - idempotency_anstest_role_absent.current | length == 0 + - idempotency_anstest_role_absent.previous | length == 0 From 6d5a138fb0e890173dd950b26a981b14d20e0f71 Mon Sep 17 00:00:00 2001 From: Sabari Jaganathan Date: Thu, 13 Jan 2022 07:55:55 +0530 Subject: [PATCH 2/3] Updated wPriv, rPriv and target_filter values. --- plugins/modules/aci_aaa_custom_privilege.py | 37 ++++++++----------- plugins/modules/aci_aaa_domain.py | 7 ++-- plugins/modules/aci_aaa_role.py | 11 +++--- .../aci_aaa_custom_privilege/tasks/main.yml | 34 ++++++++--------- 4 files changed, 42 insertions(+), 47 deletions(-) diff --git a/plugins/modules/aci_aaa_custom_privilege.py b/plugins/modules/aci_aaa_custom_privilege.py index dd5fc3bb4..4354198f1 100644 --- a/plugins/modules/aci_aaa_custom_privilege.py +++ b/plugins/modules/aci_aaa_custom_privilege.py @@ -22,7 +22,7 @@ - Name of the object class for which you are configuring access. type: str aliases: [ custom_privilege_name ] - wPriv: + w_priv: description: - Name of the custom privilege that will include write access to objects of the class. type: str @@ -51,7 +51,7 @@ custom-privilege-21, custom-privilege-22 ] - rPriv: + r_priv: description: - Name of the custom privilege that will include read access to objects of the class. type: str @@ -110,8 +110,8 @@ username: admin password: SomeSecretPassword name: fabricPod - wPriv: custom-privilege-1 - rPriv: custom-privilege-1 + w_priv: custom-privilege-1 + r_priv: custom-privilege-1 state: present delegate_to: localhost @@ -121,15 +121,15 @@ username: admin password: SomeSecretPassword name: "{{ item.name }}" - wPriv: "{{ item.wPriv }}" - rPriv: "{{ item.rPriv | default('') }}" + w_priv: "{{ item.w_priv }}" + r_priv: "{{ item.r_priv | default('') }}" state: present with_items: - name: fvTenant - wPriv: custom-privilege-2 - rPriv: custom-privilege-2 + w_priv: custom-privilege-2 + r_priv: custom-privilege-2 - name: aaaUser - wPriv: custom-privilege-3 + w_priv: custom-privilege-3 delegate_to: localhost - name: Query a custom privilege with name @@ -297,12 +297,12 @@ def main(): argument_spec = aci_argument_spec() argument_spec.update( name=dict(type="str", aliases=["custom_privilege_name"]), - wPriv=dict( + w_priv=dict( type="str", aliases=["write_priv"], choices=CUSTOM_PRIVILEGES, ), - rPriv=dict( + r_priv=dict( type="str", aliases=["read_priv"], choices=CUSTOM_PRIVILEGES, @@ -323,18 +323,13 @@ def main(): aci = ACIModule(module) name = module.params.get("name") - wPriv = module.params.get("wPriv") - rPriv = module.params.get("rPriv") + w_priv = module.params.get("w_priv") + r_priv = module.params.get("r_priv") state = module.params.get("state") name_alias = module.params.get("name_alias") aci.construct_url( - root_class=dict( - aci_class="aaaRbacClassPriv", - aci_rn="rbacdb/rbacclpriv-{0}".format(name), - module_object=name, - target_filter={"name": name}, - ), + root_class=dict(aci_class="aaaRbacClassPriv", aci_rn="rbacdb/rbacclpriv-{0}".format(name), module_object=name, target_filter=dict(name=name)), ) aci.get_existing() @@ -343,8 +338,8 @@ def main(): aci_class="aaaRbacClassPriv", class_config=dict( name=name, - wPriv=wPriv, - rPriv=rPriv, + wPriv=w_priv, + rPriv=r_priv, nameAlias=name_alias, ), ) diff --git a/plugins/modules/aci_aaa_domain.py b/plugins/modules/aci_aaa_domain.py index bd6147d67..99104b01c 100644 --- a/plugins/modules/aci_aaa_domain.py +++ b/plugins/modules/aci_aaa_domain.py @@ -248,13 +248,14 @@ def main(): aci_class="aaaDomain", aci_rn="userext/domain-{0}".format(name), module_object=name, - target_filter={"name": name}, + target_filter=dict(name=name), ), ) aci.get_existing() - restricted_rbac_domain_mapping = {False: "no", True: "yes"} - restricted_rbac_domain_state = restricted_rbac_domain_mapping.get(restricted_rbac_domain) + if state == "present": + restricted_rbac_domain_mapping = {False: "no", True: "yes"} + restricted_rbac_domain_state = restricted_rbac_domain_mapping.get(restricted_rbac_domain) aci.payload( aci_class="aaaDomain", class_config=dict( diff --git a/plugins/modules/aci_aaa_role.py b/plugins/modules/aci_aaa_role.py index d2da8aa78..835df5777 100644 --- a/plugins/modules/aci_aaa_role.py +++ b/plugins/modules/aci_aaa_role.py @@ -349,17 +349,16 @@ def main(): aci_class="aaaRole", aci_rn="userext/role-{0}".format(name), module_object=name, - target_filter={"name": name}, + target_filter=dict(name=name), ), ) aci.get_existing() - if isinstance(priv, list): - formatted_privileges = ",".join(priv) - else: - formatted_privileges = priv - if state == "present": + if isinstance(priv, list): + formatted_privileges = ",".join(priv) + else: + formatted_privileges = priv aci.payload( aci_class="aaaRole", class_config=dict( diff --git a/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml b/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml index fe9d40d0f..ac823fa8a 100644 --- a/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml +++ b/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml @@ -29,8 +29,8 @@ - name: Add a custom privilege with check mode cisco.aci.aci_aaa_custom_privilege: &cm_cp_present <<: *fabric_pod_absent - wPriv: custom-privilege-1 - rPriv: custom-privilege-1 + w_priv: custom-privilege-1 + r_priv: custom-privilege-1 state: present check_mode: yes register: cm_cp_present @@ -103,8 +103,8 @@ - name: Update fv_tenant_present custom privilege with 'custom-privilege-2' privilege cisco.aci.aci_aaa_custom_privilege: <<: *fv_tenant_present - wPriv: custom-privilege-2 - rPriv: custom-privilege-2 + w_priv: custom-privilege-2 + r_priv: custom-privilege-2 register: update_cp_with_priv - name: Assertions check for update fv_tenant_present custom privilege with 'custom-privilege-2' privilege @@ -119,24 +119,24 @@ - update_cp_with_priv.previous.0.aaaRbacClassPriv.attributes.wPriv == '' - update_cp_with_priv.previous.0.aaaRbacClassPriv.attributes.rPriv == '' -- name: Update fv_tenant_present - wPriv to 'custom-privilege-3' privilege +- name: Update fv_tenant_present - w_priv to 'custom-privilege-3' privilege cisco.aci.aci_aaa_custom_privilege: <<: *fv_tenant_present - wPriv: custom-privilege-3 - register: update_cp_wPriv_check + w_priv: custom-privilege-3 + register: update_cp_w_priv_check -- name: Assertions check for update fv_tenant_present - wPriv to 'custom-privilege-3' privilege +- name: Assertions check for update fv_tenant_present - w_priv to 'custom-privilege-3' privilege assert: that: - - update_cp_wPriv_check is changed - - update_cp_wPriv_check.current | length == 1 - - update_cp_wPriv_check.previous | length == 1 - - update_cp_wPriv_check.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' - - update_cp_wPriv_check.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-3' - - update_cp_wPriv_check.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' - - update_cp_wPriv_check.previous.0.aaaRbacClassPriv.attributes.name == 'fvTenant' - - update_cp_wPriv_check.previous.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-2' - - update_cp_wPriv_check.previous.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' + - update_cp_w_priv_check is changed + - update_cp_w_priv_check.current | length == 1 + - update_cp_w_priv_check.previous | length == 1 + - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-3' + - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' + - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-2' + - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' - name: Query a custom privilege with name cisco.aci.aci_aaa_custom_privilege: From b99ccc81d476c4076a4cdeaf0c48e3f6359e06b8 Mon Sep 17 00:00:00 2001 From: Lionel Hercot Date: Thu, 20 Oct 2022 22:00:51 -0700 Subject: [PATCH 3/3] [ignore] Fixes for aci_aaa_custom_privilege adn aci_aaa_role based on PR comments and test fixes to skip tasks not supported before ACI v5.0 --- plugins/modules/aci_aaa_custom_privilege.py | 44 +- plugins/modules/aci_aaa_role.py | 23 +- .../aci_aaa_custom_privilege/tasks/main.yml | 411 +++++++++--------- .../targets/aci_aaa_domain/tasks/main.yml | 105 +++-- .../targets/aci_aaa_role/tasks/main.yml | 38 +- 5 files changed, 356 insertions(+), 265 deletions(-) diff --git a/plugins/modules/aci_aaa_custom_privilege.py b/plugins/modules/aci_aaa_custom_privilege.py index 4354198f1..a7735e24a 100644 --- a/plugins/modules/aci_aaa_custom_privilege.py +++ b/plugins/modules/aci_aaa_custom_privilege.py @@ -13,7 +13,7 @@ DOCUMENTATION = r""" --- module: aci_aaa_custom_privilege -short_description: Manage AAA RBAC Node Rules (aaa:RbacClassPriv) +short_description: Manage AAA RBAC Custom Privileges (aaa:RbacClassPriv) description: - Manage AAA Custom Privileges with RBAC Rules on Cisco ACI fabrics. options: @@ -22,11 +22,16 @@ - Name of the object class for which you are configuring access. type: str aliases: [ custom_privilege_name ] - w_priv: + description: + description: + - Description of the AAA custom privilege. + type: str + aliases: [ descr ] + write_privilege: description: - Name of the custom privilege that will include write access to objects of the class. type: str - aliases: [ write_priv ] + aliases: [ write_priv, w_priv ] choices: [ custom-privilege-1, custom-privilege-2, @@ -51,11 +56,11 @@ custom-privilege-21, custom-privilege-22 ] - r_priv: + read_privilege: description: - Name of the custom privilege that will include read access to objects of the class. type: str - aliases: [ read_priv ] + aliases: [ read_priv, r_priv ] choices: [ custom-privilege-1, custom-privilege-2, @@ -110,8 +115,8 @@ username: admin password: SomeSecretPassword name: fabricPod - w_priv: custom-privilege-1 - r_priv: custom-privilege-1 + write_privilege: custom-privilege-1 + read_privilege: custom-privilege-1 state: present delegate_to: localhost @@ -121,15 +126,15 @@ username: admin password: SomeSecretPassword name: "{{ item.name }}" - w_priv: "{{ item.w_priv }}" - r_priv: "{{ item.r_priv | default('') }}" + write_privilege: "{{ item.write_privilege }}" + read_privilege: "{{ item.read_privilege | default('') }}" state: present with_items: - name: fvTenant - w_priv: custom-privilege-2 - r_priv: custom-privilege-2 + write_privilege: custom-privilege-2 + read_privilege: custom-privilege-2 - name: aaaUser - w_priv: custom-privilege-3 + write_privilege: custom-privilege-3 delegate_to: localhost - name: Query a custom privilege with name @@ -297,14 +302,15 @@ def main(): argument_spec = aci_argument_spec() argument_spec.update( name=dict(type="str", aliases=["custom_privilege_name"]), - w_priv=dict( + description=dict(type="str", aliases=["descr"]), + write_privilege=dict( type="str", - aliases=["write_priv"], + aliases=["write_priv", "w_priv"], choices=CUSTOM_PRIVILEGES, ), - r_priv=dict( + read_privilege=dict( type="str", - aliases=["read_priv"], + aliases=["read_priv", "r_priv"], choices=CUSTOM_PRIVILEGES, ), state=dict(type="str", default="present", choices=["absent", "present", "query"]), @@ -323,8 +329,9 @@ def main(): aci = ACIModule(module) name = module.params.get("name") - w_priv = module.params.get("w_priv") - r_priv = module.params.get("r_priv") + description = module.params.get("description") + w_priv = module.params.get("write_privilege") + r_priv = module.params.get("read_privilege") state = module.params.get("state") name_alias = module.params.get("name_alias") @@ -338,6 +345,7 @@ def main(): aci_class="aaaRbacClassPriv", class_config=dict( name=name, + descr=description, wPriv=w_priv, rPriv=r_priv, nameAlias=name_alias, diff --git a/plugins/modules/aci_aaa_role.py b/plugins/modules/aci_aaa_role.py index 835df5777..147c6512c 100644 --- a/plugins/modules/aci_aaa_role.py +++ b/plugins/modules/aci_aaa_role.py @@ -22,11 +22,11 @@ - The name of the aaa role. type: str aliases: [ aaa_role ] - priv: + privileges: description: - The privilege(s) assigned to a role. - - Use Comma separated values to assign multiple privileges to a Role. type: list + aliases: [ priv ] elements: str choices: [ admin, @@ -113,7 +113,7 @@ username: admin password: SomeSecretPassword name: anstest - priv: aaa + privileges: aaa state: present delegate_to: localhost @@ -123,14 +123,14 @@ username: admin password: SomeSecretPassword name: "{{ item.name }}" - priv: "{{ item.priv }}" + privileges: "{{ item.privilege }}" state: present delegate_to: localhost with_items: - name: anstest1 - priv: site-admin + privilege: site-admin - name: anstest2 - priv: site-policy + privilege: site-policy - name: Query a aaa role with name cisco.aci.aci_aaa_role: @@ -322,7 +322,7 @@ def main(): argument_spec = aci_argument_spec() argument_spec.update( name=dict(type="str", aliases=["aaa_role"]), - priv=dict(type="list", elements="str", choices=PRIVILEGES), + privileges=dict(type="list", aliases=["priv"], elements="str", choices=PRIVILEGES), description=dict(type="str", aliases=["descr"]), state=dict(type="str", default="present", choices=["absent", "present", "query"]), name_alias=dict(type="str"), @@ -333,13 +333,13 @@ def main(): supports_check_mode=True, required_if=[ ["state", "absent", ["name"]], - ["state", "present", ["name", "priv"]], + ["state", "present", ["name", "privileges"]], ], ) name = module.params.get("name") description = module.params.get("description") - priv = module.params.get("priv") + privileges = module.params.get("privileges") state = module.params.get("state") name_alias = module.params.get("name_alias") @@ -355,10 +355,7 @@ def main(): aci.get_existing() if state == "present": - if isinstance(priv, list): - formatted_privileges = ",".join(priv) - else: - formatted_privileges = priv + formatted_privileges = ",".join(privileges) aci.payload( aci_class="aaaRole", class_config=dict( diff --git a/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml b/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml index ac823fa8a..b04a4789d 100644 --- a/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml +++ b/tests/integration/targets/aci_aaa_custom_privilege/tasks/main.yml @@ -20,203 +20,218 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' output_level: '{{ aci_output_level | default("info") }}' -- name: Ensure fabricPod custom privilege does not exists - cisco.aci.aci_aaa_custom_privilege: &fabric_pod_absent +- name: Query system information + cisco.aci.aci_system: <<: *aci_info - name: fabricPod - state: absent - -- name: Add a custom privilege with check mode - cisco.aci.aci_aaa_custom_privilege: &cm_cp_present - <<: *fabric_pod_absent - w_priv: custom-privilege-1 - r_priv: custom-privilege-1 - state: present - check_mode: yes - register: cm_cp_present - -- name: Assertions check for add a custom privilege with check mode - assert: - that: - - cm_cp_present is changed - - cm_cp_present.current | length == 0 - - cm_cp_present.previous | length == 0 - - cm_cp_present.sent.aaaRbacClassPriv.attributes.name == 'fabricPod' - - cm_cp_present.sent.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' - - cm_cp_present.sent.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' - -- name: Add a custom privilege with normal mode - cisco.aci.aci_aaa_custom_privilege: &nm_cp_present - <<: *cm_cp_present - register: nm_cp_present - -- name: Assertions check for add a custom privilege with normal mode - assert: - that: - - nm_cp_present is changed - - nm_cp_present.current | length == 1 - - nm_cp_present.previous | length == 0 - - nm_cp_present.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' - - nm_cp_present.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' - - nm_cp_present.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' - -- name: Add a custom privilege with normal mode - idempotency works - cisco.aci.aci_aaa_custom_privilege: - <<: *nm_cp_present - register: idempotency_cp_present - -- name: Idempotency assertions check for add a custom privilege with normal mode - assert: - that: - - idempotency_cp_present is not changed - - idempotency_cp_present.current | length == 1 - - idempotency_cp_present.previous | length == 1 - - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' - - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' - - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' - - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' - - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' - - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' - -- name: Ensure fvTenant custom privilege does not exists - cisco.aci.aci_aaa_custom_privilege: &fv_tenant_absent - <<: *aci_info - name: fvTenant - state: absent - -- name: Add a custom privilege with none privileges - cisco.aci.aci_aaa_custom_privilege: &fv_tenant_present - <<: *fv_tenant_absent - state: present - register: cp_with_none_priv - -- name: Assertions check for add a custom privilege with none privileges - assert: - that: - - cp_with_none_priv is changed - - cp_with_none_priv.current | length == 1 - - cp_with_none_priv.previous | length == 0 - - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' - - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.wPriv == '' - - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.rPriv == '' - -- name: Update fv_tenant_present custom privilege with 'custom-privilege-2' privilege - cisco.aci.aci_aaa_custom_privilege: - <<: *fv_tenant_present - w_priv: custom-privilege-2 - r_priv: custom-privilege-2 - register: update_cp_with_priv - -- name: Assertions check for update fv_tenant_present custom privilege with 'custom-privilege-2' privilege - assert: - that: - - update_cp_with_priv is changed - - update_cp_with_priv.current | length == 1 - - update_cp_with_priv.previous | length == 1 - - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' - - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-2' - - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' - - update_cp_with_priv.previous.0.aaaRbacClassPriv.attributes.wPriv == '' - - update_cp_with_priv.previous.0.aaaRbacClassPriv.attributes.rPriv == '' - -- name: Update fv_tenant_present - w_priv to 'custom-privilege-3' privilege - cisco.aci.aci_aaa_custom_privilege: - <<: *fv_tenant_present - w_priv: custom-privilege-3 - register: update_cp_w_priv_check - -- name: Assertions check for update fv_tenant_present - w_priv to 'custom-privilege-3' privilege - assert: - that: - - update_cp_w_priv_check is changed - - update_cp_w_priv_check.current | length == 1 - - update_cp_w_priv_check.previous | length == 1 - - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' - - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-3' - - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' - - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.name == 'fvTenant' - - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-2' - - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' - -- name: Query a custom privilege with name - cisco.aci.aci_aaa_custom_privilege: - <<: *aci_info - name: fabricPod - state: query - register: query_a_cp_with_name - -- name: Assertions check for query a custom privilege with name - assert: - that: - - query_a_cp_with_name is not changed - - query_a_cp_with_name.current | length == 1 - - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' - - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' - - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' - -- name: Query all custom privileges - cisco.aci.aci_aaa_custom_privilege: - <<: *aci_info - state: query - register: query_all_cp - -- name: Assertions check for query all custom privileges - assert: - that: - - query_all_cp is not changed - - query_all_cp.current | length >= 2 - -- name: Remove a custom privilege with check mode - cisco.aci.aci_aaa_custom_privilege: &cm_cp_absent - <<: *aci_info - name: fabricPod - state: absent - check_mode: yes - register: cm_cp_absent - -- name: Assertions check for remove a custom privilege with check mode - assert: - that: - - cm_cp_absent is changed - - cm_cp_absent.current | length == 1 - - cm_cp_absent.previous | length == 1 - - cm_cp_absent.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' - - cm_cp_absent.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' - -- name: Remove a custom privilege with normal mode - cisco.aci.aci_aaa_custom_privilege: &nm_cp_absent - <<: *cm_cp_absent - register: nm_cp_absent - -- name: Assertions check for remove a custom privilege with normal mode - assert: - that: - - nm_cp_absent is changed - - nm_cp_absent.current == [] - - nm_cp_absent.previous | length == 1 - - nm_cp_absent.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' - -- name: Remove a custom privilege with normal mode - idempotency works - cisco.aci.aci_aaa_custom_privilege: - <<: *nm_cp_absent - register: idempotency_cp_absent - -- name: Idempotency assertions check for remove a custom privilege with normal mode - assert: - that: - - idempotency_cp_absent is not changed - - idempotency_cp_absent.current == [] - - idempotency_cp_absent.previous == [] - -- name: Query a Removed custom privilege with name - cisco.aci.aci_aaa_custom_privilege: - <<: *aci_info - name: fabricPod + id: 1 state: query - register: removed_cp_query_result - -- name: Assertions check for Removed custom privilege with name - assert: - that: - - removed_cp_query_result is not changed - - removed_cp_query_result.current == [] + register: version + +- name: Execute tasks only for ACI v5+ + when: version.current.0.topSystem.attributes.version is version('5', '>=') + block: # block specifies execution of tasks within, based on conditions + - name: Ensure fabricPod custom privilege does not exists + cisco.aci.aci_aaa_custom_privilege: &fabric_pod_absent + <<: *aci_info + name: fabricPod + state: absent + + - name: Add a custom privilege with check mode + cisco.aci.aci_aaa_custom_privilege: &cm_cp_present + <<: *fabric_pod_absent + description: My Custom Privilege + write_privilege: custom-privilege-1 + read_privilege: custom-privilege-1 + state: present + check_mode: yes + register: cm_cp_present + + - name: Assertions check for add a custom privilege with check mode + assert: + that: + - cm_cp_present is changed + - cm_cp_present.current | length == 0 + - cm_cp_present.previous | length == 0 + - cm_cp_present.sent.aaaRbacClassPriv.attributes.name == 'fabricPod' + - cm_cp_present.sent.aaaRbacClassPriv.attributes.descr == 'My Custom Privilege' + - cm_cp_present.sent.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - cm_cp_present.sent.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + + - name: Add a custom privilege with normal mode + cisco.aci.aci_aaa_custom_privilege: &nm_cp_present + <<: *cm_cp_present + register: nm_cp_present + + - name: Assertions check for add a custom privilege with normal mode + assert: + that: + - nm_cp_present is changed + - nm_cp_present.current | length == 1 + - nm_cp_present.previous | length == 0 + - nm_cp_present.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - nm_cp_present.current.0.aaaRbacClassPriv.attributes.descr == 'My Custom Privilege' + - nm_cp_present.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - nm_cp_present.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + + - name: Add a custom privilege with normal mode - idempotency works + cisco.aci.aci_aaa_custom_privilege: + <<: *nm_cp_present + register: idempotency_cp_present + + - name: Idempotency assertions check for add a custom privilege with normal mode + assert: + that: + - idempotency_cp_present is not changed + - idempotency_cp_present.current | length == 1 + - idempotency_cp_present.previous | length == 1 + - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.descr == 'My Custom Privilege' + - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - idempotency_cp_present.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.descr == 'My Custom Privilege' + - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - idempotency_cp_present.previous.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + + - name: Ensure fvTenant custom privilege does not exists + cisco.aci.aci_aaa_custom_privilege: &fv_tenant_absent + <<: *aci_info + name: fvTenant + state: absent + + - name: Add a custom privilege with none privileges + cisco.aci.aci_aaa_custom_privilege: &fv_tenant_present + <<: *fv_tenant_absent + state: present + register: cp_with_none_priv + + - name: Assertions check for add a custom privilege with none privileges + assert: + that: + - cp_with_none_priv is changed + - cp_with_none_priv.current | length == 1 + - cp_with_none_priv.previous | length == 0 + - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.wPriv == '' + - cp_with_none_priv.current.0.aaaRbacClassPriv.attributes.rPriv == '' + + - name: Update fv_tenant_present custom privilege with 'custom-privilege-2' privilege + cisco.aci.aci_aaa_custom_privilege: + <<: *fv_tenant_present + write_privilege: custom-privilege-2 + read_privilege: custom-privilege-2 + register: update_cp_with_priv + + - name: Assertions check for update fv_tenant_present custom privilege with 'custom-privilege-2' privilege + assert: + that: + - update_cp_with_priv is changed + - update_cp_with_priv.current | length == 1 + - update_cp_with_priv.previous | length == 1 + - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-2' + - update_cp_with_priv.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' + - update_cp_with_priv.previous.0.aaaRbacClassPriv.attributes.wPriv == '' + - update_cp_with_priv.previous.0.aaaRbacClassPriv.attributes.rPriv == '' + + - name: Update fv_tenant_present - write_privilege to 'custom-privilege-3' privilege + cisco.aci.aci_aaa_custom_privilege: + <<: *fv_tenant_present + write_privilege: custom-privilege-3 + register: update_cp_w_priv_check + + - name: Assertions check for update fv_tenant_present - write_privilege to 'custom-privilege-3' privilege + assert: + that: + - update_cp_w_priv_check is changed + - update_cp_w_priv_check.current | length == 1 + - update_cp_w_priv_check.previous | length == 1 + - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-3' + - update_cp_w_priv_check.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' + - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.name == 'fvTenant' + - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-2' + - update_cp_w_priv_check.previous.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-2' + + - name: Query a custom privilege with name + cisco.aci.aci_aaa_custom_privilege: + <<: *aci_info + name: fabricPod + state: query + register: query_a_cp_with_name + + - name: Assertions check for query a custom privilege with name + assert: + that: + - query_a_cp_with_name is not changed + - query_a_cp_with_name.current | length == 1 + - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.wPriv == 'custom-privilege-1' + - query_a_cp_with_name.current.0.aaaRbacClassPriv.attributes.rPriv == 'custom-privilege-1' + + - name: Query all custom privileges + cisco.aci.aci_aaa_custom_privilege: + <<: *aci_info + state: query + register: query_all_cp + + - name: Assertions check for query all custom privileges + assert: + that: + - query_all_cp is not changed + - query_all_cp.current | length >= 2 + + - name: Remove a custom privilege with check mode + cisco.aci.aci_aaa_custom_privilege: &cm_cp_absent + <<: *aci_info + name: fabricPod + state: absent + check_mode: yes + register: cm_cp_absent + + - name: Assertions check for remove a custom privilege with check mode + assert: + that: + - cm_cp_absent is changed + - cm_cp_absent.current | length == 1 + - cm_cp_absent.previous | length == 1 + - cm_cp_absent.current.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + - cm_cp_absent.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + + - name: Remove a custom privilege with normal mode + cisco.aci.aci_aaa_custom_privilege: &nm_cp_absent + <<: *cm_cp_absent + register: nm_cp_absent + + - name: Assertions check for remove a custom privilege with normal mode + assert: + that: + - nm_cp_absent is changed + - nm_cp_absent.current == [] + - nm_cp_absent.previous | length == 1 + - nm_cp_absent.previous.0.aaaRbacClassPriv.attributes.name == 'fabricPod' + + - name: Remove a custom privilege with normal mode - idempotency works + cisco.aci.aci_aaa_custom_privilege: + <<: *nm_cp_absent + register: idempotency_cp_absent + + - name: Idempotency assertions check for remove a custom privilege with normal mode + assert: + that: + - idempotency_cp_absent is not changed + - idempotency_cp_absent.current == [] + - idempotency_cp_absent.previous == [] + + - name: Query a Removed custom privilege with name + cisco.aci.aci_aaa_custom_privilege: + <<: *aci_info + name: fabricPod + state: query + register: removed_cp_query_result + + - name: Assertions check for Removed custom privilege with name + assert: + that: + - removed_cp_query_result is not changed + - removed_cp_query_result.current == [] diff --git a/tests/integration/targets/aci_aaa_domain/tasks/main.yml b/tests/integration/targets/aci_aaa_domain/tasks/main.yml index 3fa6ecdb5..9361451cd 100644 --- a/tests/integration/targets/aci_aaa_domain/tasks/main.yml +++ b/tests/integration/targets/aci_aaa_domain/tasks/main.yml @@ -20,6 +20,13 @@ 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 + - name: Ensure anstest_sec_domain does not exists cisco.aci.aci_aaa_domain: &sec_domain_absent <<: *aci_info @@ -54,7 +61,12 @@ - nm_sec_domain_present.current | length == 1 - nm_sec_domain_present.previous | length == 0 - nm_sec_domain_present.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + +- name: Assertions check for nm_sec_domain_present in v5.0+ + assert: + that: - nm_sec_domain_present.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + when: version.current.0.topSystem.attributes.version is version('5', '>=') - name: Add anstest_sec_domain security domain with normal mode - idempotency works cisco.aci.aci_aaa_domain: @@ -68,43 +80,51 @@ - idempotency_sec_domain_present.current | length == 1 - idempotency_sec_domain_present.previous | length == 1 - idempotency_sec_domain_present.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' - - idempotency_sec_domain_present.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' - idempotency_sec_domain_present.previous.0.aaaDomain.attributes.name == 'anstest_sec_domain' - - idempotency_sec_domain_present.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' - -- name: Update anstest_sec_domain security domain restricted_rbac_domain state to true - cisco.aci.aci_aaa_domain: - <<: *nm_sec_domain_present - restricted_rbac_domain: true - register: update_sec_domain_present_true - -- name: Assertions check for update anstest_sec_domain security domain restricted_rbac_domain state to true - assert: - that: - - update_sec_domain_present_true is changed - - update_sec_domain_present_true.current | length == 1 - - update_sec_domain_present_true.previous | length == 1 - - update_sec_domain_present_true.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' - - update_sec_domain_present_true.current.0.aaaDomain.attributes.restrictedRbacDomain == 'yes' - - update_sec_domain_present_true.sent.aaaDomain.attributes.restrictedRbacDomain == 'yes' - - update_sec_domain_present_true.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' - -- name: Update anstest_sec_domain security domain restricted_rbac_domain state to false - cisco.aci.aci_aaa_domain: - <<: *nm_sec_domain_present - restricted_rbac_domain: false - register: update_sec_domain_present_false -- name: Assertions check for update anstest_sec_domain security domain restricted_rbac_domain state to false +- name: Assertions check for idempotency_sec_domain_present in v5.0+ assert: that: - - update_sec_domain_present_false is changed - - update_sec_domain_present_false.current | length == 1 - - update_sec_domain_present_false.previous | length == 1 - - update_sec_domain_present_false.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' - - update_sec_domain_present_false.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' - - update_sec_domain_present_false.sent.aaaDomain.attributes.restrictedRbacDomain == 'no' - - update_sec_domain_present_false.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'yes' + - idempotency_sec_domain_present.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + - idempotency_sec_domain_present.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + when: version.current.0.topSystem.attributes.version is version('5', '>=') + +- name: Execute tasks only for ACI v5+ + when: version.current.0.topSystem.attributes.version is version('5', '>=') + block: # block specifies execution of tasks within, based on conditions + - name: Update anstest_sec_domain security domain restricted_rbac_domain state to true + cisco.aci.aci_aaa_domain: + <<: *nm_sec_domain_present + restricted_rbac_domain: true + register: update_sec_domain_present_true + + - name: Assertions check for update anstest_sec_domain security domain restricted_rbac_domain state to true + assert: + that: + - update_sec_domain_present_true is changed + - update_sec_domain_present_true.current | length == 1 + - update_sec_domain_present_true.previous | length == 1 + - update_sec_domain_present_true.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - update_sec_domain_present_true.current.0.aaaDomain.attributes.restrictedRbacDomain == 'yes' + - update_sec_domain_present_true.sent.aaaDomain.attributes.restrictedRbacDomain == 'yes' + - update_sec_domain_present_true.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + + - name: Update anstest_sec_domain security domain restricted_rbac_domain state to false + cisco.aci.aci_aaa_domain: + <<: *nm_sec_domain_present + restricted_rbac_domain: false + register: update_sec_domain_present_false + + - name: Assertions check for update anstest_sec_domain security domain restricted_rbac_domain state to false + assert: + that: + - update_sec_domain_present_false is changed + - update_sec_domain_present_false.current | length == 1 + - update_sec_domain_present_false.previous | length == 1 + - update_sec_domain_present_false.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' + - update_sec_domain_present_false.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + - update_sec_domain_present_false.sent.aaaDomain.attributes.restrictedRbacDomain == 'no' + - update_sec_domain_present_false.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'yes' - name: Query a security domain with name cisco.aci.aci_aaa_domain: @@ -119,9 +139,14 @@ - query_sec_domain_with_name is not changed - query_sec_domain_with_name.current | length == 1 - query_sec_domain_with_name.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' - - query_sec_domain_with_name.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' - query_sec_domain_with_name.current.0.aaaDomain.attributes.descr == 'Anstest Sec Domain Descr' +- name: Assertions check for query_sec_domain_with_name in v5.0+ + assert: + that: + - query_sec_domain_with_name.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + when: version.current.0.topSystem.attributes.version is version('5', '>=') + - name: Query all security domains cisco.aci.aci_aaa_domain: <<: *aci_info @@ -148,9 +173,14 @@ - cm_sec_domain_absent.current | length == 1 - cm_sec_domain_absent.previous | length == 1 - cm_sec_domain_absent.current.0.aaaDomain.attributes.name == 'anstest_sec_domain' - - cm_sec_domain_absent.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' - cm_sec_domain_absent.previous.0.aaaDomain.attributes.name == 'anstest_sec_domain' + +- name: Assertions check for cm_sec_domain_absent in v5.0+ + assert: + that: + - cm_sec_domain_absent.current.0.aaaDomain.attributes.restrictedRbacDomain == 'no' - cm_sec_domain_absent.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + when: version.current.0.topSystem.attributes.version is version('5', '>=') - name: Remove anstest_sec_domain security domain with normal mode cisco.aci.aci_aaa_domain: &nm_sec_domain_absent @@ -164,7 +194,12 @@ - nm_sec_domain_absent.current | length == 0 - nm_sec_domain_absent.previous | length == 1 - nm_sec_domain_absent.previous.0.aaaDomain.attributes.name == 'anstest_sec_domain' + +- name: Assertions check for nm_sec_domain_absent in v5.0+ + assert: + that: - nm_sec_domain_absent.previous.0.aaaDomain.attributes.restrictedRbacDomain == 'no' + when: version.current.0.topSystem.attributes.version is version('5', '>=') - name: Remove anstest_sec_domain security domain with normal mode - idempotency works cisco.aci.aci_aaa_domain: diff --git a/tests/integration/targets/aci_aaa_role/tasks/main.yml b/tests/integration/targets/aci_aaa_role/tasks/main.yml index 5c58e5f2e..4ef47b980 100644 --- a/tests/integration/targets/aci_aaa_role/tasks/main.yml +++ b/tests/integration/targets/aci_aaa_role/tasks/main.yml @@ -20,6 +20,13 @@ 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 + - name: Ensure an anstest_role aaa role does not exists cisco.aci.aci_aaa_role: &anstest_role_absent <<: *aci_info @@ -87,9 +94,21 @@ - anstest_role_with_list_priv.previous | length == 1 - anstest_role_with_list_priv.current.0.aaaRole.attributes.name == 'anstest_role' - anstest_role_with_list_priv.previous.0.aaaRole.attributes.name == 'anstest_role' - - anstest_role_with_list_priv.current.0.aaaRole.attributes.priv == 'aaa,admin,tenant-connectivity' - anstest_role_with_list_priv.previous.0.aaaRole.attributes.priv == 'aaa' +- name: Assertions check for anstest_role_with_list_priv on v4.2 or earlier + assert: + that: + - anstest_role_with_list_priv.current.0.aaaRole.attributes.priv == 'aaa,admin' + when: version.current.0.topSystem.attributes.version is version('5', '<') + +- name: Assertions check for anstest_role_with_list_priv on v5+ + assert: + that: + - anstest_role_with_list_priv.current.0.aaaRole.attributes.priv == 'aaa,admin,tenant-connectivity' + when: version.current.0.topSystem.attributes.version is version('5', '>=') + + - name: Update an anstest_role with admin privilege cisco.aci.aci_aaa_role: <<: *nm_anstest_role_present @@ -105,7 +124,18 @@ - anstest_role_with_valid_priv.current.0.aaaRole.attributes.name == 'anstest_role' - anstest_role_with_valid_priv.previous.0.aaaRole.attributes.name == 'anstest_role' - anstest_role_with_valid_priv.current.0.aaaRole.attributes.priv == 'admin' + +- name: Assertions check for anstest_role_with_valid_priv on v4.2 or earlier + assert: + that: + - anstest_role_with_valid_priv.previous.0.aaaRole.attributes.priv == 'aaa,admin' + when: version.current.0.topSystem.attributes.version is version('5', '<') + +- name: Assertions check for anstest_role_with_valid_priv on v5+ + assert: + that: - anstest_role_with_valid_priv.previous.0.aaaRole.attributes.priv == 'aaa,admin,tenant-connectivity' + when: version.current.0.topSystem.attributes.version is version('5', '>=') - name: Update an anstest_role with invalid list of privileges cisco.aci.aci_aaa_role: @@ -121,6 +151,12 @@ register: anstest_role_with_invalid_priv ignore_errors: yes +- name: Assertions check for query a aaa role with name + assert: + that: + - anstest_role_with_invalid_list_priv.msg.startswith("value of privileges must be one or more of") + - anstest_role_with_invalid_priv.msg.startswith("value of privileges must be one or more of") + - name: Query an aaa role with name cisco.aci.aci_aaa_role: <<: *aci_info