diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index a22c68a3c..374c9206f 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -344,6 +344,7 @@ def main(): module = AnsibleModule( argument_spec=argument_spec, + supports_check_mode=True, mutually_exclusive=[["content", "src"]], ) @@ -410,34 +411,48 @@ def main(): aci.url = update_qsl(aci.url, {"rsp-subtree": "modified"}) method = aci.params.get("method").upper() - # Perform request - resp, info = aci.api_call(method, aci.url, data=payload, return_response=True) + if not aci.module.check_mode: + resp, info = aci.api_call(method, aci.url, data=payload, return_response=True) + # Report failure + if info.get("status") != 200: + try: + # APIC error + aci.response_type(info["body"], rest_type) + aci.fail_json(msg="APIC Error {code}: {text}".format_map(aci.error)) + except KeyError: + # Connection error + aci.fail_json(msg="Connection failed for {url}. {msg}".format_map(info)) - # Report failure - if info.get("status") != 200: try: - # APIC error - aci.response_type(info["body"], rest_type) - aci.fail_json(msg="APIC Error {code}: {text}".format_map(aci.error)) - except KeyError: - # Connection error - aci.fail_json(msg="Connection failed for {url}. {msg}".format_map(info)) - - try: - aci.response_type(resp.read(), rest_type) - except AttributeError: - aci.response_type(info.get("body"), rest_type) - - aci.result["status"] = aci.status - aci.result["imdata"] = aci.imdata - aci.result["totalCount"] = aci.totalCount - - if aci.params.get("method") != "get": + aci.response_type(resp.read(), rest_type) + except AttributeError: + aci.response_type(info.get("body"), rest_type) + + aci.result["status"] = aci.status + aci.result["imdata"] = aci.imdata + aci.result["totalCount"] = aci.totalCount + + else: + aci.method = method + # Set changed to true so check_mode changed result is behaving similar to non aci_rest modules + aci.result["changed"] = True + + # Only set proposed if we have a payload and thus also only allow output_path if we have a payload + # DELETE and GET do not have a payload + if payload and method == "POST": + if rest_type == "json": + payload = json.loads(payload) + + aci.result["proposed"] = payload + output_path = aci.params.get("output_path") if output_path is not None: with open(output_path, "a") as output_file: - output_file.write(str(payload)) + if rest_type == "json": + json.dump([payload], output_file) + else: + output_file.write(str(payload)) # Report success aci.exit_json(**aci.result) diff --git a/tests/integration/targets/aci_rest/tasks/error_handling.yml b/tests/integration/targets/aci_rest/tasks/error_handling.yml index c8adfe2a4..54b2cbb98 100644 --- a/tests/integration/targets/aci_rest/tasks/error_handling.yml +++ b/tests/integration/targets/aci_rest/tasks/error_handling.yml @@ -31,7 +31,6 @@ - "'current' not in error_on_name_resolution" - "'previous' not in error_on_name_resolution" - "'sent' not in error_on_name_resolution" - - "'proposed' not in error_on_name_resolution" - "'filter_string' not in error_on_name_resolution" - name: Error when required parameter is missing @@ -59,7 +58,6 @@ - "'current' not in error_on_missing_required_param" - "'previous' not in error_on_missing_required_param" - "'sent' not in error_on_missing_required_param" - - "'proposed' not in error_on_missing_required_param" - "'filter_string' not in error_on_missing_required_param" - name: Error when attributes are missing @@ -90,7 +88,6 @@ - "'current' not in error_on_missing_attributes" - "'previous' not in error_on_missing_attributes" - "'sent' not in error_on_missing_attributes" - - "'proposed' not in error_on_missing_attributes" - "'filter_string' not in error_on_missing_attributes" - name: Error when input does not validate @@ -123,7 +120,6 @@ - "'current' not in error_on_input_validation" - "'previous' not in error_on_input_validation" - "'sent' not in error_on_input_validation" - - "'proposed' not in error_on_input_validation" - "'filter_string' not in error_on_input_validation" - name: Error when invalid attributes are used @@ -156,7 +152,6 @@ - "'current' not in error_on_invalid_attributes" - "'previous' not in error_on_invalid_attributes" - "'sent' not in error_on_invalid_attributes" - - "'proposed' not in error_on_invalid_attributes" - "'filter_string' not in error_on_invalid_attributes" - name: Error on invalid object @@ -188,7 +183,6 @@ - "'current' not in error_on_invalid_object" - "'previous' not in error_on_invalid_object" - "'sent' not in error_on_invalid_object" - - "'proposed' not in error_on_invalid_object" # Test case for certificate based error issue: https://github.com/CiscoDevNet/ansible-aci/issues/339 # Original error was with ospfCtxPol but same behaviour detected for tenant creation thus simplifying the test case diff --git a/tests/integration/targets/aci_rest/tasks/json_inline.yml b/tests/integration/targets/aci_rest/tasks/json_inline.yml index 731c749c3..3d5c9be48 100644 --- a/tests/integration/targets/aci_rest/tasks/json_inline.yml +++ b/tests/integration/targets/aci_rest/tasks/json_inline.yml @@ -18,7 +18,7 @@ method: delete # ADD TENANT -- name: Add tenant (normal mode) +- name: Add tenant (check mode) cisco.aci.aci_rest: &tenant_present host: '{{ aci_hostname }}' username: '{{ aci_username }}' @@ -37,6 +37,31 @@ } } } + register: cm_add_tenant + check_mode: true + +- name: Verify checkmode did not create tenant + cisco.aci.aci_tenant: + 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: ansible_test + state: query + register: cm_verify_checkmode_tenant + +- name: Verify checkmode POST operation + assert: + that: + - cm_add_tenant is changed + - cm_add_tenant.proposed.fvTenant.attributes.name == "ansible_test" + - cm_verify_checkmode_tenant.current == [] + +- name: Add tenant (normal mode) + cisco.aci.aci_rest: *tenant_present register: nm_add_tenant - name: Add tenant again (normal mode) diff --git a/tests/integration/targets/aci_rest/tasks/json_string.yml b/tests/integration/targets/aci_rest/tasks/json_string.yml index bbbd57cd4..7a1dfd8ce 100644 --- a/tests/integration/targets/aci_rest/tasks/json_string.yml +++ b/tests/integration/targets/aci_rest/tasks/json_string.yml @@ -18,7 +18,7 @@ method: delete # ADD TENANT -- name: Add tenant (normal mode) +- name: Add tenant (check mode) cisco.aci.aci_rest: &tenant_present host: '{{ aci_hostname }}' username: '{{ aci_username }}' @@ -38,6 +38,31 @@ } } output_path: "/tmp/ansible_output_file.log" + register: cm_add_tenant + check_mode: true + +- name: Verify checkmode did not create tenant + cisco.aci.aci_tenant: + 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: ansible_test + state: query + register: cm_verify_checkmode_tenant + +- name: Verify checkmode POST operation + assert: + that: + - cm_add_tenant is changed + - cm_add_tenant.proposed.fvTenant.attributes.name == "ansible_test" + - cm_verify_checkmode_tenant.current == [] + +- name: Add tenant (normal mode) + cisco.aci.aci_rest: *tenant_present register: nm_add_tenant - name: Add tenant again (normal mode) diff --git a/tests/integration/targets/aci_rest/tasks/xml_file.yml b/tests/integration/targets/aci_rest/tasks/xml_file.yml index cdde037c4..fea63112b 100644 --- a/tests/integration/targets/aci_rest/tasks/xml_file.yml +++ b/tests/integration/targets/aci_rest/tasks/xml_file.yml @@ -32,10 +32,25 @@ check_mode: true register: cm_add_tenant +- name: Verify checkmode did not create tenant + cisco.aci.aci_tenant: + 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: ans_test_create + state: query + register: cm_verify_checkmode_tenant + - name: Assertions check for add tenant using ans_test_create xml template file with check mode assert: that: - - cm_add_tenant is not changed + - cm_add_tenant is changed + - '"ans_test_create" in cm_add_tenant.proposed' + - cm_verify_checkmode_tenant.current == [] - name: Add tenant using ans_test_create xml template file with normal mode cisco.aci.aci_rest: @@ -95,7 +110,7 @@ - name: Assertions check for update tenant description using ans_test_update xml template file with check mode assert: that: - - cm_update_tenant is not changed + - cm_update_tenant is changed - name: Update tenant description using ans_test_update xml template file with normal mode cisco.aci.aci_rest: @@ -168,7 +183,7 @@ - name: Assertions check for delete tenant using ans_test_delete xml template file with check mode assert: that: - - cm_delete_tenant is not changed + - cm_delete_tenant is changed - name: Delete tenant using ans_test_delete xml template file with normal mode cisco.aci.aci_rest: diff --git a/tests/integration/targets/aci_rest/tasks/xml_string.yml b/tests/integration/targets/aci_rest/tasks/xml_string.yml index c87291779..d1424bafa 100644 --- a/tests/integration/targets/aci_rest/tasks/xml_string.yml +++ b/tests/integration/targets/aci_rest/tasks/xml_string.yml @@ -18,6 +18,66 @@ method: delete # ADD TENANT +- name: Add tenant (check mode) + cisco.aci.aci_rest: + 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 + method: post + content: + + register: cm_add_tenant + check_mode: true + +- name: Add tenant 2 (check mode) + cisco.aci.aci_rest: + 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 + method: post + content: + { + "fvTenant": { + "attributes": { + "name": "ansible_test" + } + } + } + register: cm_add_tenant_2 + check_mode: true + +- name: Verify checkmode did not create tenant + cisco.aci.aci_tenant: &tenant_cm_query + 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: ansible_test + state: query + register: cm_verify_checkmode_tenant + +- name: Verify checkmode POST operation + assert: + that: + - cm_add_tenant is changed + - '"ansible_test" in cm_add_tenant.proposed' + - cm_add_tenant_2 is changed + - '"ansible_test" in cm_add_tenant.proposed' + - cm_verify_checkmode_tenant.current == [] + - name: Add tenant (normal mode) cisco.aci.aci_rest: &tenant_present host: '{{ aci_hostname }}' diff --git a/tests/integration/targets/aci_rest/tasks/yaml_inline.yml b/tests/integration/targets/aci_rest/tasks/yaml_inline.yml index d7538f9a6..6cd06afcd 100644 --- a/tests/integration/targets/aci_rest/tasks/yaml_inline.yml +++ b/tests/integration/targets/aci_rest/tasks/yaml_inline.yml @@ -18,7 +18,7 @@ method: delete # ADD TENANT -- name: Add tenant (normal mode) +- name: Add tenant (check mode) cisco.aci.aci_rest: &tenant_present host: '{{ aci_hostname }}' username: '{{ aci_username }}' @@ -33,6 +33,31 @@ fvTenant: attributes: name: ansible_test + register: cm_add_tenant + check_mode: true + +- name: Verify checkmode did not create tenant + cisco.aci.aci_tenant: + 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: ansible_test + state: query + register: cm_verify_checkmode_tenant + +- name: Verify checkmode POST operation + assert: + that: + - cm_add_tenant is changed + - cm_add_tenant.proposed.fvTenant.attributes.name == "ansible_test" + - cm_verify_checkmode_tenant.current == [] + +- name: Add tenant (normal mode) + cisco.aci.aci_rest: *tenant_present register: nm_add_tenant - name: Add tenant again (normal mode) diff --git a/tests/integration/targets/aci_rest/tasks/yaml_string.yml b/tests/integration/targets/aci_rest/tasks/yaml_string.yml index d7538f9a6..895045474 100644 --- a/tests/integration/targets/aci_rest/tasks/yaml_string.yml +++ b/tests/integration/targets/aci_rest/tasks/yaml_string.yml @@ -18,7 +18,7 @@ method: delete # ADD TENANT -- name: Add tenant (normal mode) +- name: Add tenant (check mode) cisco.aci.aci_rest: &tenant_present host: '{{ aci_hostname }}' username: '{{ aci_username }}' @@ -29,10 +29,35 @@ output_level: '{{ aci_output_level | default("info") }}' path: /api/mo/uni.json method: post - content: + content: | fvTenant: attributes: name: ansible_test + register: cm_add_tenant + check_mode: true + +- name: Verify checkmode did not create tenant + cisco.aci.aci_tenant: + 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: ansible_test + state: query + register: cm_verify_checkmode_tenant + +- name: Verify checkmode POST operation + assert: + that: + - cm_add_tenant is changed + - cm_add_tenant.proposed.fvTenant.attributes.name == "ansible_test" + - cm_verify_checkmode_tenant.current == [] + +- name: Add tenant (normal mode) + cisco.aci.aci_rest: *tenant_present register: nm_add_tenant - name: Add tenant again (normal mode)