2017-07-17 15:32:12 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2018-02-21 18:35:59 +00:00
|
|
|
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
|
2017-07-29 21:14:16 +00:00
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
2017-07-17 15:32:12 +00:00
|
|
|
|
2017-08-16 03:16:38 +00:00
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
2017-07-17 15:32:12 +00:00
|
|
|
'status': ['preview'],
|
|
|
|
'supported_by': 'community'}
|
|
|
|
|
|
|
|
DOCUMENTATION = r'''
|
2017-08-17 00:27:42 +00:00
|
|
|
---
|
2017-07-17 15:32:12 +00:00
|
|
|
module: aci_rest
|
|
|
|
short_description: Direct access to the Cisco APIC REST API
|
|
|
|
description:
|
|
|
|
- Enables the management of the Cisco ACI fabric through direct access to the Cisco APIC REST API.
|
2018-02-21 18:35:59 +00:00
|
|
|
- Thanks to the idempotent nature of the APIC, this module is idempotent and reports changes.
|
|
|
|
notes:
|
|
|
|
- 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 in :ref:`the ACI documentation <aci_guide_known_issues>`.
|
|
|
|
- Certain payloads (and 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. A workaround for this issue exists.
|
|
|
|
More information in :ref:`the ACI documentation <aci_guide_known_issues>`.
|
|
|
|
- XML payloads require the C(lxml) and C(xmljson) python libraries. For JSON payloads nothing special is needed.
|
2017-07-17 15:32:12 +00:00
|
|
|
- More information regarding the Cisco APIC REST API is available from
|
|
|
|
U(http://www.cisco.com/c/en/us/td/docs/switches/datacenter/aci/apic/sw/2-x/rest_cfg/2_1_x/b_Cisco_APIC_REST_API_Configuration_Guide.html).
|
|
|
|
author:
|
|
|
|
- Dag Wieers (@dagwieers)
|
|
|
|
version_added: '2.4'
|
|
|
|
requirements:
|
2018-01-21 09:59:08 +00:00
|
|
|
- lxml (when using XML payload)
|
|
|
|
- xmljson >= 0.1.8 (when using XML payload)
|
2017-07-17 15:32:12 +00:00
|
|
|
- python 2.7+ (when using xmljson)
|
|
|
|
options:
|
|
|
|
method:
|
|
|
|
description:
|
|
|
|
- The HTTP method of the request.
|
|
|
|
- Using C(delete) is typically used for deleting objects.
|
|
|
|
- Using C(get) is typically used for querying objects.
|
|
|
|
- Using C(post) is typically used for modifying objects.
|
|
|
|
default: get
|
|
|
|
choices: [ delete, get, post ]
|
|
|
|
aliases: [ action ]
|
|
|
|
path:
|
|
|
|
description:
|
|
|
|
- URI being used to execute API calls.
|
|
|
|
- Must end in C(.xml) or C(.json).
|
2017-07-31 11:44:27 +00:00
|
|
|
required: yes
|
2017-07-17 15:32:12 +00:00
|
|
|
aliases: [ uri ]
|
|
|
|
content:
|
|
|
|
description:
|
2018-01-21 09:59:08 +00:00
|
|
|
- When used instead of C(src), sets the payload of the API request directly.
|
2017-07-17 15:32:12 +00:00
|
|
|
- This may be convenient to template simple requests, for anything complex use the M(template) module.
|
|
|
|
src:
|
|
|
|
description:
|
|
|
|
- Name of the absolute path of the filname that includes the body
|
|
|
|
of the http request being sent to the ACI fabric.
|
|
|
|
aliases: [ config_file ]
|
2018-02-21 18:35:59 +00:00
|
|
|
extends_documentation_fragment: aci
|
2017-07-17 15:32:12 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = r'''
|
2018-01-07 23:44:30 +00:00
|
|
|
- name: Add a tenant using certifcate authentication
|
2017-07-17 15:32:12 +00:00
|
|
|
aci_rest:
|
2018-02-20 02:27:17 +00:00
|
|
|
host: apic
|
|
|
|
username: admin
|
2018-01-07 23:44:30 +00:00
|
|
|
private_key: pki/admin.key
|
2017-07-17 15:32:12 +00:00
|
|
|
method: post
|
|
|
|
path: /api/mo/uni.xml
|
|
|
|
src: /home/cisco/ansible/aci/configs/aci_config.xml
|
|
|
|
delegate_to: localhost
|
|
|
|
|
2017-09-05 07:20:14 +00:00
|
|
|
- name: Add a tenant using inline YAML
|
|
|
|
aci_rest:
|
2018-02-20 02:27:17 +00:00
|
|
|
host: apic
|
|
|
|
username: admin
|
2018-01-07 23:44:30 +00:00
|
|
|
private_key: pki/admin.key
|
2017-09-05 07:20:14 +00:00
|
|
|
validate_certs: no
|
2018-01-21 09:59:08 +00:00
|
|
|
path: /api/mo/uni.json
|
2017-09-05 07:20:14 +00:00
|
|
|
method: post
|
|
|
|
content:
|
|
|
|
fvTenant:
|
|
|
|
attributes:
|
|
|
|
name: Sales
|
|
|
|
descr: Sales departement
|
|
|
|
delegate_to: localhost
|
|
|
|
|
|
|
|
- name: Add a tenant using a JSON string
|
2017-09-05 06:36:39 +00:00
|
|
|
aci_rest:
|
2018-02-20 02:27:17 +00:00
|
|
|
host: apic
|
|
|
|
username: admin
|
2018-01-07 23:44:30 +00:00
|
|
|
private_key: pki/admin.key
|
2017-09-05 06:36:39 +00:00
|
|
|
validate_certs: no
|
2018-01-21 09:59:08 +00:00
|
|
|
path: /api/mo/uni.json
|
2017-09-05 06:36:39 +00:00
|
|
|
method: post
|
2018-01-21 09:59:08 +00:00
|
|
|
content:
|
2017-09-05 06:36:39 +00:00
|
|
|
{
|
|
|
|
"fvTenant": {
|
|
|
|
"attributes": {
|
|
|
|
"name": "Sales",
|
|
|
|
"descr": "Sales departement"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delegate_to: localhost
|
|
|
|
|
2017-09-05 07:20:14 +00:00
|
|
|
- name: Add a tenant using an XML string
|
|
|
|
aci_rest:
|
2018-02-20 02:27:17 +00:00
|
|
|
host: apic
|
|
|
|
username: admin
|
2018-01-07 23:44:30 +00:00
|
|
|
private_key: pki/{{ aci_username}}.key
|
2017-09-05 07:20:14 +00:00
|
|
|
validate_certs: no
|
2018-01-21 09:59:08 +00:00
|
|
|
path: /api/mo/uni.xml
|
2017-09-05 07:20:14 +00:00
|
|
|
method: post
|
2018-01-21 09:59:08 +00:00
|
|
|
content: '<fvTenant name="Sales" descr="Sales departement"/>'
|
2017-09-05 07:20:14 +00:00
|
|
|
delegate_to: localhost
|
|
|
|
|
2018-01-07 23:44:30 +00:00
|
|
|
- name: Get tenants using password authentication
|
2017-07-17 15:32:12 +00:00
|
|
|
aci_rest:
|
2018-02-20 02:27:17 +00:00
|
|
|
host: apic
|
|
|
|
username: admin
|
|
|
|
password: SomeSecretPassword
|
2017-07-17 15:32:12 +00:00
|
|
|
method: get
|
|
|
|
path: /api/node/class/fvTenant.json
|
|
|
|
delegate_to: localhost
|
|
|
|
|
|
|
|
- name: Configure contracts
|
|
|
|
aci_rest:
|
2018-02-20 02:27:17 +00:00
|
|
|
host: apic
|
|
|
|
username: admin
|
2018-01-07 23:44:30 +00:00
|
|
|
private_key: pki/admin.key
|
2017-07-17 15:32:12 +00:00
|
|
|
method: post
|
|
|
|
path: /api/mo/uni.xml
|
|
|
|
src: /home/cisco/ansible/aci/configs/contract_config.xml
|
|
|
|
delegate_to: localhost
|
|
|
|
|
|
|
|
- name: Register leaves and spines
|
|
|
|
aci_rest:
|
2018-02-20 02:27:17 +00:00
|
|
|
host: apic
|
|
|
|
username: admin
|
2018-01-07 23:44:30 +00:00
|
|
|
private_key: pki/admin.key
|
2017-07-17 15:32:12 +00:00
|
|
|
validate_certs: no
|
|
|
|
method: post
|
|
|
|
path: /api/mo/uni/controller/nodeidentpol.xml
|
|
|
|
content: |
|
|
|
|
<fabricNodeIdentPol>
|
|
|
|
<fabricNodeIdentP name="{{ item.name }}" nodeId="{{ item.nodeid }}" status="{{ item.status }}" serial="{{ item.serial }}"/>
|
|
|
|
</fabricNodeIdentPol>
|
|
|
|
with_items:
|
|
|
|
- '{{ apic_leavesspines }}'
|
|
|
|
delegate_to: localhost
|
|
|
|
|
|
|
|
- name: Wait for all controllers to become ready
|
|
|
|
aci_rest:
|
2018-02-20 02:27:17 +00:00
|
|
|
host: apic
|
|
|
|
username: admin
|
2018-01-07 23:44:30 +00:00
|
|
|
private_key: pki/admin.key
|
2017-07-17 15:32:12 +00:00
|
|
|
validate_certs: no
|
|
|
|
path: /api/node/class/topSystem.json?query-target-filter=eq(topSystem.role,"controller")
|
|
|
|
register: apics
|
|
|
|
until: "'totalCount' in apics and apics.totalCount|int >= groups['apic']|count"
|
|
|
|
retries: 120
|
|
|
|
delay: 30
|
|
|
|
delegate_to: localhost
|
|
|
|
run_once: yes
|
|
|
|
'''
|
|
|
|
|
|
|
|
RETURN = r'''
|
|
|
|
error_code:
|
|
|
|
description: The REST ACI return code, useful for troubleshooting on failure
|
|
|
|
returned: always
|
|
|
|
type: int
|
|
|
|
sample: 122
|
|
|
|
error_text:
|
|
|
|
description: The REST ACI descriptive text, useful for troubleshooting on failure
|
|
|
|
returned: always
|
|
|
|
type: string
|
|
|
|
sample: unknown managed object class foo
|
|
|
|
imdata:
|
|
|
|
description: Converted output returned by the APIC REST (register this for post-processing)
|
|
|
|
returned: always
|
|
|
|
type: string
|
|
|
|
sample: [{"error": {"attributes": {"code": "122", "text": "unknown managed object class foo"}}}]
|
|
|
|
payload:
|
|
|
|
description: The (templated) payload send to the APIC REST API (xml or json)
|
|
|
|
returned: always
|
|
|
|
type: string
|
|
|
|
sample: '<foo bar="boo"/>'
|
|
|
|
raw:
|
|
|
|
description: The raw output returned by the APIC REST API (xml or json)
|
|
|
|
returned: parse error
|
|
|
|
type: string
|
|
|
|
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
|
|
|
|
response:
|
|
|
|
description: HTTP response string
|
|
|
|
returned: always
|
|
|
|
type: string
|
|
|
|
sample: 'HTTP Error 400: Bad Request'
|
2017-07-19 07:46:16 +00:00
|
|
|
status:
|
2017-07-17 15:32:12 +00:00
|
|
|
description: HTTP status code
|
|
|
|
returned: always
|
|
|
|
type: int
|
|
|
|
sample: 400
|
|
|
|
totalCount:
|
|
|
|
description: Number of items in the imdata array
|
|
|
|
returned: always
|
|
|
|
type: string
|
|
|
|
sample: '0'
|
2017-09-05 06:36:39 +00:00
|
|
|
url:
|
|
|
|
description: URL used for APIC REST call
|
|
|
|
returned: success
|
|
|
|
type: string
|
|
|
|
sample: https://1.2.3.4/api/mo/uni/tn-[Dag].json?rsp-subtree=modified
|
2017-07-17 15:32:12 +00:00
|
|
|
'''
|
|
|
|
|
2017-09-05 07:20:14 +00:00
|
|
|
import json
|
2017-07-17 15:32:12 +00:00
|
|
|
import os
|
|
|
|
|
2017-09-05 06:36:39 +00:00
|
|
|
try:
|
|
|
|
from ansible.module_utils.six.moves.urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
|
|
|
|
HAS_URLPARSE = True
|
|
|
|
except:
|
|
|
|
HAS_URLPARSE = False
|
|
|
|
|
2017-07-17 15:32:12 +00:00
|
|
|
# Optional, only used for XML payload
|
|
|
|
try:
|
2017-11-13 16:51:18 +00:00
|
|
|
import lxml.etree # noqa
|
2017-07-17 15:32:12 +00:00
|
|
|
HAS_LXML_ETREE = True
|
|
|
|
except ImportError:
|
|
|
|
HAS_LXML_ETREE = False
|
|
|
|
|
|
|
|
# Optional, only used for XML payload
|
|
|
|
try:
|
2017-11-13 16:51:18 +00:00
|
|
|
from xmljson import cobra # noqa
|
2017-07-17 15:32:12 +00:00
|
|
|
HAS_XMLJSON_COBRA = True
|
|
|
|
except ImportError:
|
|
|
|
HAS_XMLJSON_COBRA = False
|
|
|
|
|
2017-09-05 07:20:14 +00:00
|
|
|
# Optional, only used for YAML validation
|
|
|
|
try:
|
|
|
|
import yaml
|
|
|
|
HAS_YAML = True
|
|
|
|
except:
|
|
|
|
HAS_YAML = False
|
|
|
|
|
2017-07-31 11:44:27 +00:00
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
2018-02-02 23:41:56 +00:00
|
|
|
from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec
|
2017-07-17 15:32:12 +00:00
|
|
|
from ansible.module_utils.urls import fetch_url
|
2017-09-05 07:20:14 +00:00
|
|
|
from ansible.module_utils._text import to_text
|
2017-07-17 15:32:12 +00:00
|
|
|
|
|
|
|
|
2017-09-05 06:36:39 +00:00
|
|
|
def update_qsl(url, params):
|
|
|
|
''' Add or update a URL query string '''
|
|
|
|
|
|
|
|
if HAS_URLPARSE:
|
|
|
|
url_parts = list(urlparse(url))
|
|
|
|
query = dict(parse_qsl(url_parts[4]))
|
|
|
|
query.update(params)
|
|
|
|
url_parts[4] = urlencode(query)
|
|
|
|
return urlunparse(url_parts)
|
|
|
|
elif '?' in url:
|
|
|
|
return url + '&' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()])
|
|
|
|
else:
|
|
|
|
return url + '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()])
|
|
|
|
|
|
|
|
|
2018-02-02 23:41:56 +00:00
|
|
|
class ACIRESTModule(ACIModule):
|
2017-09-05 06:36:39 +00:00
|
|
|
|
2018-02-02 23:41:56 +00:00
|
|
|
def changed(self, d):
|
|
|
|
''' Check ACI response for changes '''
|
2017-09-05 06:36:39 +00:00
|
|
|
|
2018-02-02 23:41:56 +00:00
|
|
|
if isinstance(d, dict):
|
|
|
|
for k, v in d.items():
|
|
|
|
if k == 'status' and v in ('created', 'modified', 'deleted'):
|
|
|
|
return True
|
|
|
|
elif self.changed(v) is True:
|
|
|
|
return True
|
|
|
|
elif isinstance(d, list):
|
|
|
|
for i in d:
|
|
|
|
if self.changed(i) is True:
|
|
|
|
return True
|
2017-09-05 06:36:39 +00:00
|
|
|
|
2018-02-02 23:41:56 +00:00
|
|
|
return False
|
2017-09-05 06:36:39 +00:00
|
|
|
|
2018-02-19 11:01:14 +00:00
|
|
|
def response_type(self, rawoutput, rest_type='xml'):
|
2018-02-02 23:41:56 +00:00
|
|
|
''' Handle APIC response output '''
|
2017-07-17 15:32:12 +00:00
|
|
|
|
2018-02-02 23:41:56 +00:00
|
|
|
if rest_type == 'json':
|
|
|
|
self.response_json(rawoutput)
|
|
|
|
else:
|
|
|
|
self.response_xml(rawoutput)
|
2017-07-17 15:32:12 +00:00
|
|
|
|
2018-02-02 23:41:56 +00:00
|
|
|
# Use APICs built-in idempotency
|
|
|
|
if HAS_URLPARSE:
|
|
|
|
self.result['changed'] = self.changed(self.imdata)
|
2017-09-05 06:36:39 +00:00
|
|
|
|
2017-07-17 15:32:12 +00:00
|
|
|
|
|
|
|
def main():
|
2018-01-17 16:11:30 +00:00
|
|
|
argument_spec = aci_argument_spec()
|
2017-07-31 11:44:27 +00:00
|
|
|
argument_spec.update(
|
2017-07-17 15:32:12 +00:00
|
|
|
path=dict(type='str', required=True, aliases=['uri']),
|
|
|
|
method=dict(type='str', default='get', choices=['delete', 'get', 'post'], aliases=['action']),
|
|
|
|
src=dict(type='path', aliases=['config_file']),
|
2017-09-05 07:20:14 +00:00
|
|
|
content=dict(type='raw'),
|
2018-01-21 09:59:08 +00:00
|
|
|
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
|
2017-07-17 15:32:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
module = AnsibleModule(
|
|
|
|
argument_spec=argument_spec,
|
|
|
|
mutually_exclusive=[['content', 'src']],
|
|
|
|
)
|
|
|
|
|
|
|
|
content = module.params['content']
|
2018-02-21 18:35:59 +00:00
|
|
|
path = module.params['path']
|
2017-07-17 15:32:12 +00:00
|
|
|
src = module.params['src']
|
|
|
|
|
|
|
|
# Report missing file
|
|
|
|
file_exists = False
|
|
|
|
if src:
|
|
|
|
if os.path.isfile(src):
|
|
|
|
file_exists = True
|
|
|
|
else:
|
2017-08-09 23:51:13 +00:00
|
|
|
module.fail_json(msg="Cannot find/access src '%s'" % src)
|
2017-07-17 15:32:12 +00:00
|
|
|
|
|
|
|
# Find request type
|
|
|
|
if path.find('.xml') != -1:
|
|
|
|
rest_type = 'xml'
|
|
|
|
if not HAS_LXML_ETREE:
|
|
|
|
module.fail_json(msg='The lxml python library is missing, or lacks etree support.')
|
|
|
|
if not HAS_XMLJSON_COBRA:
|
|
|
|
module.fail_json(msg='The xmljson python library is missing, or lacks cobra support.')
|
|
|
|
elif path.find('.json') != -1:
|
|
|
|
rest_type = 'json'
|
|
|
|
else:
|
2018-01-21 09:59:08 +00:00
|
|
|
module.fail_json(msg='Failed to find REST API payload type (neither .xml nor .json).')
|
2017-07-17 15:32:12 +00:00
|
|
|
|
2018-02-02 23:41:56 +00:00
|
|
|
aci = ACIRESTModule(module)
|
2017-07-17 15:32:12 +00:00
|
|
|
|
2017-09-05 06:36:39 +00:00
|
|
|
# We include the payload as it may be templated
|
|
|
|
payload = content
|
|
|
|
if file_exists:
|
2017-07-17 15:32:12 +00:00
|
|
|
with open(src, 'r') as config_object:
|
|
|
|
# TODO: Would be nice to template this, requires action-plugin
|
2017-07-31 11:44:27 +00:00
|
|
|
payload = config_object.read()
|
2017-07-17 15:32:12 +00:00
|
|
|
|
2018-01-21 09:59:08 +00:00
|
|
|
# Validate payload
|
2017-09-05 07:20:14 +00:00
|
|
|
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
|
2017-09-14 16:06:56 +00:00
|
|
|
payload = json.dumps(yaml.safe_load(payload))
|
2017-09-05 07:20:14 +00:00
|
|
|
except Exception as e:
|
2018-01-21 09:59:08 +00:00
|
|
|
module.fail_json(msg='Failed to parse provided JSON/YAML payload: %s' % to_text(e), exception=to_text(e), payload=payload)
|
2017-09-05 07:20:14 +00:00
|
|
|
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:
|
2018-01-21 09:59:08 +00:00
|
|
|
module.fail_json(msg='Failed to parse provided XML payload: %s' % to_text(e), payload=payload)
|
2017-09-05 07:20:14 +00:00
|
|
|
|
2018-01-07 23:44:30 +00:00
|
|
|
# Perform actual request using auth cookie (Same as aci_request, but also supports XML)
|
2018-02-02 23:41:56 +00:00
|
|
|
aci.url = '%(protocol)s://%(host)s/' % aci.params + path.lstrip('/')
|
2018-01-07 23:44:30 +00:00
|
|
|
if aci.params['method'] != 'get':
|
|
|
|
path += '?rsp-subtree=modified'
|
2018-02-02 23:41:56 +00:00
|
|
|
aci.url = update_qsl(aci.url, {'rsp-subtree': 'modified'})
|
2018-01-07 23:44:30 +00:00
|
|
|
|
|
|
|
# Sign and encode request as to APIC's wishes
|
|
|
|
if aci.params['private_key'] is not None:
|
|
|
|
aci.cert_auth(path=path, payload=payload)
|
|
|
|
|
2018-02-21 18:35:59 +00:00
|
|
|
aci.method = aci.params['method'].upper()
|
|
|
|
|
2018-01-07 23:44:30 +00:00
|
|
|
# Perform request
|
2018-02-02 23:41:56 +00:00
|
|
|
resp, info = fetch_url(module, aci.url,
|
2018-01-07 23:44:30 +00:00
|
|
|
data=payload,
|
|
|
|
headers=aci.headers,
|
2018-02-21 18:35:59 +00:00
|
|
|
method=aci.method,
|
2018-01-07 23:44:30 +00:00
|
|
|
timeout=aci.params['timeout'],
|
|
|
|
use_proxy=aci.params['use_proxy'])
|
2017-07-17 15:32:12 +00:00
|
|
|
|
2018-02-19 11:01:14 +00:00
|
|
|
aci.response = info['msg']
|
|
|
|
aci.status = info['status']
|
2017-07-17 15:32:12 +00:00
|
|
|
|
|
|
|
# Report failure
|
|
|
|
if info['status'] != 200:
|
|
|
|
try:
|
2018-01-07 23:44:30 +00:00
|
|
|
# APIC error
|
2018-02-19 11:01:14 +00:00
|
|
|
aci.response_type(info['body'], rest_type)
|
|
|
|
aci.fail_json(msg='APIC Error %(code)s: %(text)s' % aci.error)
|
2017-07-17 15:32:12 +00:00
|
|
|
except KeyError:
|
2018-01-07 23:44:30 +00:00
|
|
|
# Connection error
|
2018-02-19 11:01:14 +00:00
|
|
|
aci.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info)
|
2018-02-02 23:41:56 +00:00
|
|
|
|
2018-02-19 11:01:14 +00:00
|
|
|
aci.response_type(resp.read(), rest_type)
|
2017-07-31 11:44:27 +00:00
|
|
|
|
2018-02-02 23:41:56 +00:00
|
|
|
aci.result['imdata'] = aci.imdata
|
|
|
|
aci.result['totalCount'] = aci.totalCount
|
2017-07-17 15:32:12 +00:00
|
|
|
|
|
|
|
# Report success
|
2018-02-19 11:01:14 +00:00
|
|
|
aci.exit_json(**aci.result)
|
2017-07-17 15:32:12 +00:00
|
|
|
|
2017-07-29 21:14:16 +00:00
|
|
|
|
2017-07-17 15:32:12 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|