Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[minor_change] add support for checkmode in aci_rest module #470

Merged
merged 6 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions plugins/module_utils/aci.py
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,9 @@ def exit_json(self, filter_existing=None, **kwargs):
self.result["sent"] = self.config
self.result["proposed"] = self.proposed

elif self.__class__.__name__ == "ACIRESTModule" and self.method != "GET":
lhercot marked this conversation as resolved.
Show resolved Hide resolved
self.result["proposed"] = self.proposed
akinross marked this conversation as resolved.
Show resolved Hide resolved

self.dump_json()
self.result.update(**kwargs)
self.module.exit_json(**self.result)
Expand Down Expand Up @@ -1486,6 +1489,9 @@ def fail_json(self, msg, **kwargs):
self.result["sent"] = self.config
self.result["proposed"] = self.proposed

elif self.__class__.__name__ == "ACIRESTModule" and self.method != "GET":
self.result["proposed"] = self.proposed

self.result.update(**kwargs)
self.module.fail_json(msg=msg, **self.result)

Expand Down Expand Up @@ -1514,6 +1520,15 @@ def dump_json(self):
if self.result.get("changed") is True:
json.dump([mo], output_file)

elif self.__class__.__name__ == "ACIRESTModule" and self.method != "GET":
output_path = self.params.get("output_path")
if output_path is not None:
with open(output_path, "a") as output_file:
if self.module.check_mode:
json.dump([self.proposed], output_file)
else:
output_file.write(str(self.proposed))

def parsed_url_path(self, url):
if not HAS_URLPARSE:
self.fail_json(msg="urlparse is not installed")
Expand Down
55 changes: 30 additions & 25 deletions plugins/modules/aci_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ def main():

module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
mutually_exclusive=[["content", "src"]],
)

Expand Down Expand Up @@ -410,34 +411,38 @@ 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:
anvitha-jain marked this conversation as resolved.
Show resolved Hide resolved
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":
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))
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

if payload:
if rest_type == "json":
aci.proposed = json.loads(payload)
elif rest_type == "xml":
aci.proposed = payload

# Report success
aci.exit_json(**aci.result)
Expand Down
6 changes: 0 additions & 6 deletions tests/integration/targets/aci_rest/tasks/error_handling.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
27 changes: 26 additions & 1 deletion tests/integration/targets/aci_rest/tasks/json_inline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}'
Expand All @@ -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)
Expand Down
27 changes: 26 additions & 1 deletion tests/integration/targets/aci_rest/tasks/json_string.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}'
Expand All @@ -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)
Expand Down
21 changes: 18 additions & 3 deletions tests/integration/targets/aci_rest/tasks/xml_file.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
60 changes: 60 additions & 0 deletions tests/integration/targets/aci_rest/tasks/xml_string.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
<fvTenant name="ansible_test"/>
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 }}'
Expand Down
27 changes: 26 additions & 1 deletion tests/integration/targets/aci_rest/tasks/yaml_inline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}'
Expand All @@ -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)
Expand Down
Loading