
jsonutils provides multiple benefits in comparison to pure stdlib json (like using simplejson on Python 2.6). Similar patch was already merged before [1], but since it lacked hacking rule to enforce jsonutils usage, new occurrences of stdlib json module usage were introduced. This patch switches all the code to using jsonutils and adds a hacking rule to enforce the rule. The hacking rule requires that jsonutils module does not mimic as 'json' thru using import renames, so the code was updated not to rename the module when doing import. The hacking rule was shamelessly copied from the corresponding nova review [2]. [1]: https://review.openstack.org/#/c/99760/ [2]: https://review.openstack.org/111296/ Change-Id: Ie7a5bb76445e15cde9fbf9ff3d2101a014637b37
145 lines
5.0 KiB
Python
145 lines
5.0 KiB
Python
# Copyright 2014 VMware, Inc.
|
|
# All Rights Reserved
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from neutron.common import exceptions as exception
|
|
from neutron.openstack.common import jsonutils
|
|
from neutron.openstack.common import log
|
|
from neutron.plugins.vmware.api_client import exception as api_exc
|
|
from neutron.plugins.vmware.common import exceptions as nsx_exc
|
|
from neutron import version
|
|
|
|
HTTP_GET = "GET"
|
|
HTTP_POST = "POST"
|
|
HTTP_DELETE = "DELETE"
|
|
HTTP_PUT = "PUT"
|
|
# Prefix to be used for all NSX API calls
|
|
URI_PREFIX = "/ws.v1"
|
|
NEUTRON_VERSION = version.version_info.release_string()
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
def _build_uri_path(resource,
|
|
resource_id=None,
|
|
parent_resource_id=None,
|
|
fields=None,
|
|
relations=None,
|
|
filters=None,
|
|
types=None,
|
|
is_attachment=False,
|
|
extra_action=None):
|
|
resources = resource.split('/')
|
|
res_path = resources[0] + (resource_id and "/%s" % resource_id or '')
|
|
if len(resources) > 1:
|
|
# There is also a parent resource to account for in the uri
|
|
res_path = "%s/%s/%s" % (resources[1],
|
|
parent_resource_id,
|
|
res_path)
|
|
if is_attachment:
|
|
res_path = "%s/attachment" % res_path
|
|
elif extra_action:
|
|
res_path = "%s/%s" % (res_path, extra_action)
|
|
params = []
|
|
params.append(fields and "fields=%s" % fields)
|
|
params.append(relations and "relations=%s" % relations)
|
|
params.append(types and "types=%s" % types)
|
|
if filters:
|
|
sorted_filters = [
|
|
'%s=%s' % (k, filters[k]) for k in sorted(filters.keys())
|
|
]
|
|
params.extend(sorted_filters)
|
|
uri_path = "%s/%s" % (URI_PREFIX, res_path)
|
|
non_empty_params = [x for x in params if x is not None]
|
|
if non_empty_params:
|
|
query_string = '&'.join(non_empty_params)
|
|
if query_string:
|
|
uri_path += "?%s" % query_string
|
|
return uri_path
|
|
|
|
|
|
def format_exception(etype, e, exception_locals):
|
|
"""Consistent formatting for exceptions.
|
|
|
|
:param etype: a string describing the exception type.
|
|
:param e: the exception.
|
|
:param execption_locals: calling context local variable dict.
|
|
:returns: a formatted string.
|
|
"""
|
|
msg = [_("Error. %(type)s exception: %(exc)s.") %
|
|
{'type': etype, 'exc': e}]
|
|
l = dict((k, v) for k, v in exception_locals.iteritems()
|
|
if k != 'request')
|
|
msg.append(_("locals=[%s]") % str(l))
|
|
return ' '.join(msg)
|
|
|
|
|
|
def do_request(*args, **kwargs):
|
|
"""Issue a request to the cluster specified in kwargs.
|
|
|
|
:param args: a list of positional arguments.
|
|
:param kwargs: a list of keyworkds arguments.
|
|
:returns: the result of the operation loaded into a python
|
|
object or None.
|
|
"""
|
|
cluster = kwargs["cluster"]
|
|
try:
|
|
res = cluster.api_client.request(*args)
|
|
if res:
|
|
return jsonutils.loads(res)
|
|
except api_exc.ResourceNotFound:
|
|
raise exception.NotFound()
|
|
except api_exc.ReadOnlyMode:
|
|
raise nsx_exc.MaintenanceInProgress()
|
|
|
|
|
|
def get_single_query_page(path, cluster, page_cursor=None,
|
|
page_length=1000, neutron_only=True):
|
|
params = []
|
|
if page_cursor:
|
|
params.append("_page_cursor=%s" % page_cursor)
|
|
params.append("_page_length=%s" % page_length)
|
|
# NOTE(salv-orlando): On the NSX backend the 'Quantum' tag is still
|
|
# used for marking Neutron entities in order to preserve compatibility
|
|
if neutron_only:
|
|
params.append("tag_scope=quantum")
|
|
query_params = "&".join(params)
|
|
path = "%s%s%s" % (path, "&" if (path.find("?") != -1) else "?",
|
|
query_params)
|
|
body = do_request(HTTP_GET, path, cluster=cluster)
|
|
# Result_count won't be returned if _page_cursor is supplied
|
|
return body['results'], body.get('page_cursor'), body.get('result_count')
|
|
|
|
|
|
def get_all_query_pages(path, cluster):
|
|
need_more_results = True
|
|
result_list = []
|
|
page_cursor = None
|
|
while need_more_results:
|
|
results, page_cursor = get_single_query_page(
|
|
path, cluster, page_cursor)[:2]
|
|
if not page_cursor:
|
|
need_more_results = False
|
|
result_list.extend(results)
|
|
return result_list
|
|
|
|
|
|
def mk_body(**kwargs):
|
|
"""Convenience function creates and dumps dictionary to string.
|
|
|
|
:param kwargs: the key/value pirs to be dumped into a json string.
|
|
:returns: a json string.
|
|
"""
|
|
return jsonutils.dumps(kwargs, ensure_ascii=False)
|