aci_rest: Accept YAML input and validate content input

This PR includes:
- Accept inline YAML, YAML string, inline JSON, JSON string input and
  XML string input
- Validate YAML, JSON and XML input
This commit is contained in:
Dag Wieers 2017-09-05 00:20:14 -07:00
parent a796391c9b
commit a200ecf966

View file

@ -70,7 +70,22 @@ EXAMPLES = r'''
src: /home/cisco/ansible/aci/configs/aci_config.xml src: /home/cisco/ansible/aci/configs/aci_config.xml
delegate_to: localhost delegate_to: localhost
- name: Add a tenant - name: Add a tenant using inline YAML
aci_rest:
hostname: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
validate_certs: no
path: /api/mo/uni/tn-[Sales].json
method: post
content:
fvTenant:
attributes:
name: Sales
descr: Sales departement
delegate_to: localhost
- name: Add a tenant using a JSON string
aci_rest: aci_rest:
hostname: '{{ inventory_hostname }}' hostname: '{{ inventory_hostname }}'
username: '{{ aci_username }}' username: '{{ aci_username }}'
@ -89,6 +104,18 @@ EXAMPLES = r'''
} }
delegate_to: localhost delegate_to: localhost
- name: Add a tenant using an XML string
aci_rest:
hostname: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
validate_certs: no
path: /api/mo/uni/tn-[Sales].xml
method: post
content: |
<fvTenant name="Sales" descr="Sales departement"/>
delegate_to: localhost
- name: Get tenants - name: Get tenants
aci_rest: aci_rest:
hostname: '{{ inventory_hostname }}' hostname: '{{ inventory_hostname }}'
@ -187,6 +214,7 @@ url:
sample: https://1.2.3.4/api/mo/uni/tn-[Dag].json?rsp-subtree=modified sample: https://1.2.3.4/api/mo/uni/tn-[Dag].json?rsp-subtree=modified
''' '''
import json
import os import os
try: try:
@ -211,9 +239,17 @@ try:
except ImportError: except ImportError:
HAS_XMLJSON_COBRA = False HAS_XMLJSON_COBRA = False
# Optional, only used for YAML validation
try:
import yaml
HAS_YAML = True
except:
HAS_YAML = False
from ansible.module_utils.aci import ACIModule, aci_argument_spec, aci_response_json, aci_response_xml from ansible.module_utils.aci import ACIModule, aci_argument_spec, aci_response_json, aci_response_xml
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_text
def update_qsl(url, params): def update_qsl(url, params):
@ -267,7 +303,7 @@ def main():
path=dict(type='str', required=True, aliases=['uri']), path=dict(type='str', required=True, aliases=['uri']),
method=dict(type='str', default='get', choices=['delete', 'get', 'post'], aliases=['action']), method=dict(type='str', default='get', choices=['delete', 'get', 'post'], aliases=['action']),
src=dict(type='path', aliases=['config_file']), src=dict(type='path', aliases=['config_file']),
content=dict(type='str'), content=dict(type='raw'),
) )
module = AnsibleModule( module = AnsibleModule(
@ -311,6 +347,30 @@ def main():
# TODO: Would be nice to template this, requires action-plugin # TODO: Would be nice to template this, requires action-plugin
payload = config_object.read() payload = config_object.read()
# Validate content
if rest_type == 'json':
if content and isinstance(content, dict):
# Validate inline YAML/JSON
payload = json.dumps(payload)
elif payload and isinstance(payload, str) and HAS_YAML:
try:
# Validate YAML/JSON string
payload = json.dumps(yaml.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)
elif rest_type == 'xml' and HAS_LXML_ETREE:
if content and isinstance(content, dict) and HAS_XMLJSON_COBRA:
# Validate inline YAML/JSON
# FIXME: Converting from a dictionary to XML is unsupported at this time
# payload = etree.tostring(payload)
pass
elif payload and isinstance(payload, str):
try:
# 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)
# Perform actual request using auth cookie (Same as aci_request,but also supports XML) # Perform actual request using auth cookie (Same as aci_request,but also supports XML)
url = '%(protocol)s://%(hostname)s/' % aci.params + path.lstrip('/') url = '%(protocol)s://%(hostname)s/' % aci.params + path.lstrip('/')
if method != 'get': if method != 'get':
@ -325,9 +385,9 @@ def main():
if info['status'] != 200: if info['status'] != 200:
try: try:
aci_response(aci.result, info['body'], rest_type) aci_response(aci.result, info['body'], rest_type)
module.fail_json(msg='Request failed: %(error_code)s %(error_text)s' % aci.result, **aci.result) module.fail_json(msg='Request failed: %(error_code)s %(error_text)s' % aci.result, payload=payload, **aci.result)
except KeyError: except KeyError:
module.fail_json(msg='Request failed for %(url)s. %(msg)s' % info, **aci.result) module.fail_json(msg='Request failed for %(url)s. %(msg)s' % info, payload=payload, **aci.result)
aci_response(aci.result, resp.read(), rest_type) aci_response(aci.result, resp.read(), rest_type)