diff --git a/lib/ansible/modules/network/aci/aci_rest.py b/lib/ansible/modules/network/aci/aci_rest.py index 38217cdf88..42b79dfbcd 100644 --- a/lib/ansible/modules/network/aci/aci_rest.py +++ b/lib/ansible/modules/network/aci/aci_rest.py @@ -22,8 +22,8 @@ author: - Dag Wieers (@dagwieers) version_added: '2.4' requirements: -- lxml (when using XML content) -- xmljson >= 0.1.8 (when using XML content) +- lxml (when using XML payload) +- xmljson >= 0.1.8 (when using XML payload) - python 2.7+ (when using xmljson) extends_documentation_fragment: aci options: @@ -45,7 +45,7 @@ options: aliases: [ uri ] content: description: - - When used instead of C(src), sets the content of the API request directly. + - When used instead of C(src), sets the payload of the API request directly. - This may be convenient to template simple requests, for anything complex use the M(template) module. src: description: @@ -53,8 +53,12 @@ options: of the http request being sent to the ACI fabric. aliases: [ config_file ] notes: -- When using inline-JSON (using C(content)), YAML requires to start with a blank line. - Otherwise the JSON statement will be parsed as a YAML mapping (dictionary) and translated into invalid JSON as a result. +- Certain payloads are known not to be idempotent, so be careful when constructing payloads, + e.g. using C(status="created") will cause idempotency issues, use C(status="modified") instead. + More information at U(https://github.com/ansible/community/wiki/Network:-ACI-Documentation#known-issues) +- Certain payloads (or used paths) are known to report no changes happened when changes did happen. + This is a known APIC problem and has been reported to the vendor. + More information at U(https://github.com/ansible/community/wiki/Network:-ACI-Documentation#known-issues) - XML payloads require the C(lxml) and C(xmljson) python libraries. For JSON payloads nothing special is needed. ''' @@ -75,7 +79,7 @@ EXAMPLES = r''' username: '{{ aci_username }}' private_key: pki/admin.key validate_certs: no - path: /api/mo/uni/tn-[Sales].json + path: /api/mo/uni.json method: post content: fvTenant: @@ -90,9 +94,9 @@ EXAMPLES = r''' username: '{{ aci_username }}' private_key: pki/admin.key validate_certs: no - path: /api/mo/uni/tn-[Sales].json + path: /api/mo/uni.json method: post - content: | + content: { "fvTenant": { "attributes": { @@ -109,10 +113,9 @@ EXAMPLES = r''' username: '{{ aci_username }}' private_key: pki/{{ aci_username}}.key validate_certs: no - path: /api/mo/uni/tn-[Sales].xml + path: /api/mo/uni.xml method: post - content: | - + content: '' delegate_to: localhost - name: Get tenants using password authentication @@ -301,6 +304,7 @@ def main(): method=dict(type='str', default='get', choices=['delete', 'get', 'post'], aliases=['action']), src=dict(type='path', aliases=['config_file']), content=dict(type='raw'), + protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6 ) module = AnsibleModule( @@ -330,7 +334,7 @@ def main(): elif path.find('.json') != -1: rest_type = 'json' else: - module.fail_json(msg='Failed to find REST API content type (neither .xml nor .json).') + module.fail_json(msg='Failed to find REST API payload type (neither .xml nor .json).') aci = ACIModule(module) @@ -341,7 +345,7 @@ def main(): # TODO: Would be nice to template this, requires action-plugin payload = config_object.read() - # Validate content + # Validate payload if rest_type == 'json': if content and isinstance(content, dict): # Validate inline YAML/JSON @@ -351,7 +355,7 @@ def main(): # Validate YAML/JSON string payload = json.dumps(yaml.safe_load(payload)) except Exception as e: - module.fail_json(msg='Failed to parse provided JSON/YAML content: %s' % to_text(e), exception=to_text(e), payload=payload) + module.fail_json(msg='Failed to parse provided JSON/YAML payload: %s' % to_text(e), exception=to_text(e), payload=payload) elif rest_type == 'xml' and HAS_LXML_ETREE: if content and isinstance(content, dict) and HAS_XMLJSON_COBRA: # Validate inline YAML/JSON @@ -363,7 +367,7 @@ def main(): # Validate XML string payload = lxml.etree.tostring(lxml.etree.fromstring(payload)) except Exception as e: - module.fail_json(msg='Failed to parse provided XML content: %s' % to_text(e), payload=payload) + module.fail_json(msg='Failed to parse provided XML payload: %s' % to_text(e), payload=payload) # Perform actual request using auth cookie (Same as aci_request, but also supports XML) aci.result['url'] = '%(protocol)s://%(hostname)s/' % aci.params + path.lstrip('/') diff --git a/test/integration/targets/aci_rest/tasks/json_inline.yml b/test/integration/targets/aci_rest/tasks/json_inline.yml index bc456f0ec5..32be221ffc 100644 --- a/test/integration/targets/aci_rest/tasks/json_inline.yml +++ b/test/integration/targets/aci_rest/tasks/json_inline.yml @@ -30,7 +30,7 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].json + path: /api/mo/uni.json method: post content: { @@ -63,7 +63,7 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].json + path: /api/mo/uni.json method: post content: { diff --git a/test/integration/targets/aci_rest/tasks/json_string.yml b/test/integration/targets/aci_rest/tasks/json_string.yml index fe77edfa91..3b0a2e57cb 100644 --- a/test/integration/targets/aci_rest/tasks/json_string.yml +++ b/test/integration/targets/aci_rest/tasks/json_string.yml @@ -19,7 +19,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: delete - delegate_to: localhost # ADD TENANT - name: Add tenant (normal mode) @@ -30,7 +29,7 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].json + path: /api/mo/uni.json method: post content: | { @@ -40,12 +39,10 @@ } } } - delegate_to: localhost register: nm_add_tenant - name: Add tenant again (normal mode) aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again - name: Verify add_tenant @@ -63,7 +60,7 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].json + path: /api/mo/uni.json method: post content: | { @@ -74,12 +71,10 @@ } } } - delegate_to: localhost register: nm_add_tenant_descr - name: Change description of tenant again (normal mode) aci_rest: *tenant_changed - delegate_to: localhost register: nm_add_tenant_descr_again - name: Verify add_tenant_descr @@ -91,7 +86,6 @@ # ADD TENANT AGAIN - name: Add tenant again with no description (normal mode) aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again_no_descr - name: Verify add_tenant_again_no_descr @@ -110,7 +104,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: get - delegate_to: localhost register: nm_query_all_tenants - name: Verify query_all_tenants @@ -129,7 +122,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: get - delegate_to: localhost register: nm_query_tenant - name: Verify query_tenant @@ -140,12 +132,10 @@ # REMOVE TENANT - name: Remove tenant (normal mode) aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant - name: Remove tenant again (normal mode) aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant_again - name: Verify remove_tenant @@ -157,7 +147,6 @@ # QUERY NON-EXISTING TENANT - name: Query non-existing tenant (normal mode) aci_rest: *tenant_query - delegate_to: localhost register: nm_query_non_tenant - name: Verify query_non_tenant diff --git a/test/integration/targets/aci_rest/tasks/xml_string.yml b/test/integration/targets/aci_rest/tasks/xml_string.yml index b6ed0c9423..bb062fbd5a 100644 --- a/test/integration/targets/aci_rest/tasks/xml_string.yml +++ b/test/integration/targets/aci_rest/tasks/xml_string.yml @@ -19,7 +19,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].xml method: delete - delegate_to: localhost # ADD TENANT - name: Add tenant (normal mode) @@ -30,16 +29,14 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].xml + path: /api/mo/uni.xml method: post content: | - delegate_to: localhost register: nm_add_tenant - name: Add tenant again (normal mode) aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again - name: Verify add_tenant @@ -57,16 +54,14 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].xml + path: /api/mo/uni.xml method: post content: | - delegate_to: localhost register: nm_add_tenant_descr - name: Change description of tenant again (normal mode) aci_rest: *tenant_changed - delegate_to: localhost register: nm_add_tenant_descr_again - name: Verify add_tenant_descr @@ -78,7 +73,6 @@ # ADD TENANT AGAIN - name: Add tenant again with no description (normal mode) aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again_no_descr - name: Verify add_tenant_again_no_descr @@ -97,7 +91,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].xml method: get - delegate_to: localhost register: nm_query_all_tenants - name: Verify query_all_tenants @@ -116,7 +109,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].xml method: get - delegate_to: localhost register: nm_query_tenant - name: Verify query_tenant @@ -127,12 +119,10 @@ # REMOVE TENANT - name: Remove tenant (normal mode) aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant - name: Remove tenant again (normal mode) aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant_again - name: Verify remove_tenant @@ -144,7 +134,6 @@ # QUERY NON-EXISTING TENANT - name: Query non-existing tenant (normal mode) aci_rest: *tenant_query - delegate_to: localhost register: nm_query_non_tenant - name: Verify query_non_tenant diff --git a/test/integration/targets/aci_rest/tasks/yaml_inline.yml b/test/integration/targets/aci_rest/tasks/yaml_inline.yml index 3520a06442..6f404ef503 100644 --- a/test/integration/targets/aci_rest/tasks/yaml_inline.yml +++ b/test/integration/targets/aci_rest/tasks/yaml_inline.yml @@ -19,7 +19,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: delete - delegate_to: localhost # ADD TENANT - name: Add tenant (normal mode) @@ -30,18 +29,16 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].json + path: /api/mo/uni.json method: post content: fvTenant: attributes: name: ansible_test - delegate_to: localhost register: nm_add_tenant - name: Add tenant again (normal mode) aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again - name: Verify add_tenant @@ -59,19 +56,17 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].json + path: /api/mo/uni.json method: post content: fvTenant: attributes: name: ansible_test descr: Ansible test tenant - delegate_to: localhost register: nm_add_tenant_descr - name: Change description of tenant again (normal mode) aci_rest: *tenant_changed - delegate_to: localhost register: nm_add_tenant_descr_again - name: Verify add_tenant_descr @@ -83,7 +78,6 @@ # ADD TENANT AGAIN - name: Add tenant again with no description (normal mode) aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again_no_descr - name: Verify add_tenant_again_no_descr @@ -102,7 +96,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: get - delegate_to: localhost register: nm_query_all_tenants - name: Verify query_all_tenants @@ -121,7 +114,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: get - delegate_to: localhost register: nm_query_tenant - name: Verify query_tenant @@ -132,12 +124,10 @@ # REMOVE TENANT - name: Remove tenant (normal mode) aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant - name: Remove tenant again (normal mode) aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant_again - name: Verify remove_tenant @@ -149,7 +139,6 @@ # QUERY NON-EXISTING TENANT - name: Query non-existing tenant (normal mode) aci_rest: *tenant_query - delegate_to: localhost register: nm_query_non_tenant - name: Verify query_non_tenant diff --git a/test/integration/targets/aci_rest/tasks/yaml_string.yml b/test/integration/targets/aci_rest/tasks/yaml_string.yml index 0d48031e0b..48f9c701d7 100644 --- a/test/integration/targets/aci_rest/tasks/yaml_string.yml +++ b/test/integration/targets/aci_rest/tasks/yaml_string.yml @@ -19,7 +19,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: delete - delegate_to: localhost # ADD TENANT - name: Add tenant (normal mode) @@ -30,18 +29,16 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].json + path: /api/mo/uni.json method: post content: | fvTenant: attributes: name: ansible_test - delegate_to: localhost register: nm_add_tenant - name: Add tenant again (normal mode) aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again - name: Verify add_tenant @@ -59,19 +56,17 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - path: /api/mo/uni/tn-[ansible_test].json + path: /api/mo/uni.json method: post content: | fvTenant: attributes: name: ansible_test descr: Ansible test tenant - delegate_to: localhost register: nm_add_tenant_descr - name: Change description of tenant again (normal mode) aci_rest: *tenant_changed - delegate_to: localhost register: nm_add_tenant_descr_again - name: Verify add_tenant_descr @@ -83,7 +78,6 @@ # ADD TENANT AGAIN - name: Add tenant again with no description (normal mode) aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again_no_descr - name: Verify add_tenant_again_no_descr @@ -102,7 +96,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: get - delegate_to: localhost register: nm_query_all_tenants - name: Verify query_all_tenants @@ -121,7 +114,6 @@ use_proxy: '{{ aci_use_proxy | default(true) }}' path: /api/mo/uni/tn-[ansible_test].json method: get - delegate_to: localhost register: nm_query_tenant - name: Verify query_tenant @@ -132,12 +124,10 @@ # REMOVE TENANT - name: Remove tenant (normal mode) aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant - name: Remove tenant again (normal mode) aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant_again - name: Verify remove_tenant @@ -149,7 +139,6 @@ # QUERY NON-EXISTING TENANT - name: Query non-existing tenant (normal mode) aci_rest: *tenant_query - delegate_to: localhost register: nm_query_non_tenant - name: Verify query_non_tenant diff --git a/test/sanity/validate-modules/ignore.txt b/test/sanity/validate-modules/ignore.txt index 9d7d69a6a1..82e8246f48 100644 --- a/test/sanity/validate-modules/ignore.txt +++ b/test/sanity/validate-modules/ignore.txt @@ -287,6 +287,7 @@ lib/ansible/modules/network/aci/aci_intf_policy_mcp.py E322 lib/ansible/modules/network/aci/aci_intf_policy_port_channel.py E322 lib/ansible/modules/network/aci/aci_intf_policy_port_security.py E322 lib/ansible/modules/network/aci/aci_l3out_route_tag_policy.py E322 +lib/ansible/modules/network/aci/aci_rest.py E322 lib/ansible/modules/network/aci/aci_taboo_contract.py E322 lib/ansible/modules/network/aci/aci_tenant.py E322 lib/ansible/modules/network/aci/aci_tenant_action_rule_profile.py E322