diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 97311df9f..8ab1b94a6 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -61,6 +61,14 @@ - Preserve the response for the provided path. type: bool default: false + page_size: + description: + - The number of items to return in a single page. + type: int + page: + description: + - The page number to return. + type: int extends_documentation_fragment: - cisco.aci.aci - cisco.aci.annotation @@ -163,6 +171,17 @@ delegate_to: localhost register: query_result +- name: Get first 5 tenants using password authentication and pagination + cisco.aci.aci_rest: + host: apic + username: admin + password: SomeSecretPassword + method: get + page_size: 5 + path: /api/node/class/fvTenant.json + delegate_to: localhost + register: query_result + - name: Configure contracts cisco.aci.aci_rest: host: apic @@ -377,6 +396,8 @@ def main(): src=dict(type="path", aliases=["config_file"]), content=dict(type="raw"), rsp_subtree_preserve=dict(type="bool", default=False), + page_size=dict(type="int"), + page=dict(type="int"), ) module = AnsibleModule( @@ -390,6 +411,10 @@ def main(): src = module.params.get("src") rsp_subtree_preserve = module.params.get("rsp_subtree_preserve") annotation = module.params.get("annotation") + page_size = module.params.get("page_size") + page = module.params.get("page") + if module.params.get("method") != "get" and page_size: + module.fail_json(msg="Pagination parameters (page and page_size) are only valid for GET method") # Report missing file file_exists = False @@ -453,6 +478,10 @@ def main(): # NOTE By setting aci.path we ensure that Ansible displays accurate URL info when the plugin and the aci_rest module are used. aci.path = path.lstrip("/") aci.url = "{0}/{1}".format(aci.base_url, aci.path) + + if aci.params.get("method") == "get" and page_size: + aci.path = update_qsl(aci.path, {"page": page, "page-size": page_size}) + aci.url = update_qsl(aci.url, {"page": page, "page-size": page_size}) if aci.params.get("method") != "get" and not rsp_subtree_preserve: aci.path = "{0}?rsp-subtree=modified".format(aci.path) aci.url = update_qsl(aci.url, {"rsp-subtree": "modified"}) diff --git a/tests/integration/targets/aci_rest/tasks/json_inline.yml b/tests/integration/targets/aci_rest/tasks/json_inline.yml index 6e60bf26e..ceac67b6c 100644 --- a/tests/integration/targets/aci_rest/tasks/json_inline.yml +++ b/tests/integration/targets/aci_rest/tasks/json_inline.yml @@ -17,6 +17,11 @@ path: /api/mo/uni/tn-[ansible_test].json method: delete +- name: Remove tenant 2 + cisco.aci.aci_rest: + <<: *tenant_absent + path: /api/mo/uni/tn-[ansible_test_2].json + # ADD TENANT - name: Add tenant (check mode) cisco.aci.aci_rest: &tenant_present @@ -138,6 +143,72 @@ that: - nm_query_all_tenants is not changed +# ADD TENANT 2 +- name: Add tenant 2 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_present + content: + { + "fvTenant": { + "attributes": { + "descr": "Ansible test tenant", + "name": "ansible_test_2" + } + } + } + +# QUERY ALL TENANTS WITH PAGINATION +- name: Query all tenants with pagination (normal mode) + cisco.aci.aci_rest: &tenant_query_all_paginated + 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") }}' + path: /api/class/uni/fvTenant.json + page_size: 10 + method: get + register: nm_query_all_tenants_paginated + +- name: Query all tenant with pagination - Size 1 / Page 0 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 1 + register: nm_query_all_tenants_paginated_1_0 + +- name: Query all tenant with pagination - Size 1 / Page 1 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 1 + page: 1 + register: nm_query_all_tenants_paginated_1_1 + +- name: Query all tenant with pagination - Size 2 / Page 0 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 2 + register: nm_query_all_tenants_paginated_2_0 + +- name: Verify query_all_tenants_paginated + ansible.builtin.assert: + that: + - nm_query_all_tenants_paginated is not changed + - nm_query_all_tenants_paginated_1_0 is not changed + - nm_query_all_tenants_paginated_1_1 is not changed + - nm_query_all_tenants_paginated_2_0 is not changed + +- name: Verify pagination works as expected + ansible.builtin.assert: + that: + - nm_query_all_tenants_paginated is not changed + - nm_query_all_tenants_paginated_1_0.imdata | length == 1 + - nm_query_all_tenants_paginated_1_1.imdata | length == 1 + - nm_query_all_tenants_paginated_2_0.imdata | length == 2 + - nm_query_all_tenants_paginated_1_0.imdata.0.fvTenant.attributes.name == nm_query_all_tenants_paginated_2_0.imdata.0.fvTenant.attributes.name + - nm_query_all_tenants_paginated_1_1.imdata.0.fvTenant.attributes.name == nm_query_all_tenants_paginated_2_0.imdata.1.fvTenant.attributes.name + # QUERY A TENANT - name: Query our tenant cisco.aci.aci_rest: &tenant_query @@ -162,6 +233,12 @@ cisco.aci.aci_rest: *tenant_absent register: nm_remove_tenant +- name: Remove tenant_2 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_absent + path: /api/mo/uni/tn-[ansible_test_2].json + register: nm_remove_tenant_2 + - name: Remove tenant again (normal mode) cisco.aci.aci_rest: *tenant_absent register: nm_remove_tenant_again diff --git a/tests/integration/targets/aci_rest/tasks/json_string.yml b/tests/integration/targets/aci_rest/tasks/json_string.yml index 9efa19c71..0903d5355 100644 --- a/tests/integration/targets/aci_rest/tasks/json_string.yml +++ b/tests/integration/targets/aci_rest/tasks/json_string.yml @@ -17,6 +17,11 @@ path: /api/mo/uni/tn-[ansible_test].json method: delete +- name: Remove tenant 2 + cisco.aci.aci_rest: + <<: *tenant_absent + path: /api/mo/uni/tn-[ansible_test_2].json + # ADD TENANT - name: Add tenant (check mode) cisco.aci.aci_rest: &tenant_present @@ -139,6 +144,76 @@ that: - nm_query_all_tenants is not changed + +# ADD TENANT 2 +- name: Add tenant 2 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_present + content: | + { + "fvTenant": { + "attributes": { + "descr": "Ansible test tenant", + "name": "ansible_test_2" + } + } + } + +# QUERY ALL TENANTS WITH PAGINATION +- name: Query all tenants with pagination (normal mode) + cisco.aci.aci_rest: &tenant_query_all_paginated + 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") }}' + path: /api/class/uni/fvTenant.json + page_size: 10 + page: 0 + method: get + register: nm_query_all_tenants_paginated + +- name: Query all tenant with pagination - Size 1 / Page 0 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 1 + page: 0 + register: nm_query_all_tenants_paginated_1_0 + +- name: Query all tenant with pagination - Size 1 / Page 1 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 1 + page: 1 + register: nm_query_all_tenants_paginated_1_1 + +- name: Query all tenant with pagination - Size 2 / Page 0 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 2 + page: 0 + register: nm_query_all_tenants_paginated_2_0 + +- name: Verify query_all_tenants_paginated + ansible.builtin.assert: + that: + - nm_query_all_tenants_paginated is not changed + - nm_query_all_tenants_paginated_1_0 is not changed + - nm_query_all_tenants_paginated_1_1 is not changed + - nm_query_all_tenants_paginated_2_0 is not changed + +- name: Verify pagination works as expected + ansible.builtin.assert: + that: + - nm_query_all_tenants_paginated is not changed + - nm_query_all_tenants_paginated_1_0.imdata | length == 1 + - nm_query_all_tenants_paginated_1_1.imdata | length == 1 + - nm_query_all_tenants_paginated_2_0.imdata | length == 2 + - nm_query_all_tenants_paginated_1_0.imdata.0.fvTenant.attributes.name == nm_query_all_tenants_paginated_2_0.imdata.0.fvTenant.attributes.name + - nm_query_all_tenants_paginated_1_1.imdata.0.fvTenant.attributes.name == nm_query_all_tenants_paginated_2_0.imdata.1.fvTenant.attributes.name + # QUERY A TENANT - name: Query our tenant cisco.aci.aci_rest: &tenant_query diff --git a/tests/integration/targets/aci_rest/tasks/xml_string.yml b/tests/integration/targets/aci_rest/tasks/xml_string.yml index e3e627e3e..37c79ef26 100644 --- a/tests/integration/targets/aci_rest/tasks/xml_string.yml +++ b/tests/integration/targets/aci_rest/tasks/xml_string.yml @@ -17,6 +17,18 @@ path: /api/mo/uni/tn-[ansible_test].xml method: delete +- name: Remove tenant2 + cisco.aci.aci_rest: &tenant_absent_2 + 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") }}' + path: /api/mo/uni/tn-[ansible_test_2].xml + method: delete + # ADD TENANT - name: Add tenant (check mode) cisco.aci.aci_rest: @@ -184,6 +196,71 @@ that: - nm_query_all_tenants is not changed +# ADD TENANT 2 +- name: Add tenant 2 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_present + path: /api/mo/uni.xml + content: + + register: nm_add_tenant_2 + +# QUERY ALL TENANTS WITH PAGINATION +- name: Query all tenants with pagination (normal mode) + cisco.aci.aci_rest: &tenant_query_all_paginated + 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") }}' + path: /api/mo/uni.xml + page_size: 10 + page: 0 + method: get + register: nm_query_all_tenants_paginated + + +- name: Query all tenant with pagination - Size 1 / Page 0 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 1 + page: 0 + register: nm_query_all_tenants_paginated_1_0 + +- name: Query all tenant with pagination - Size 1 / Page 1 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 1 + page: 1 + register: nm_query_all_tenants_paginated_1_1 + +- name: Query all tenant with pagination - Size 2 / Page 0 (normal mode) + cisco.aci.aci_rest: + <<: *tenant_query_all_paginated + page_size: 2 + page: 0 + register: nm_query_all_tenants_paginated_2_0 + +- name: Verify query_all_tenants_paginated + ansible.builtin.assert: + that: + - nm_query_all_tenants_paginated is not changed + - nm_query_all_tenants_paginated_1_0 is not changed + - nm_query_all_tenants_paginated_1_1 is not changed + - nm_query_all_tenants_paginated_2_0 is not changed + +- name: Verify pagination works as expected + ansible.builtin.assert: + that: + - nm_query_all_tenants_paginated is not changed + - nm_query_all_tenants_paginated_1_0.imdata | length == 1 + - nm_query_all_tenants_paginated_1_1.imdata | length == 1 + - nm_query_all_tenants_paginated_2_0.imdata | length == 2 + - nm_query_all_tenants_paginated_1_0.imdata.0.fvTenant.attributes.name == nm_query_all_tenants_paginated_2_0.imdata.0.fvTenant.attributes.name + - nm_query_all_tenants_paginated_1_1.imdata.0.fvTenant.attributes.name == nm_query_all_tenants_paginated_2_0.imdata.1.fvTenant.attributes.name + # QUERY A TENANT - name: Query our tenant cisco.aci.aci_rest: &tenant_query