Repository URL to install this package:
Version:
4.2.30 ▾
|
# Copyright 2018 TrilioData Inc.
# All Rights Reserved.
import collections
import functools
import itertools
import re
from oslo_log import log as logging
from oslo_utils import strutils
import six
import six.moves.urllib.parse as urlparse
import webob
from webob import exc
from dmapi.api.openstack import api_version_request
import dmapi.conf
from dmapi import exception
from dmapi.i18n import _
from dmapi import utils
CONF = dmapi.conf.CONF
LOG = logging.getLogger(__name__)
def get_id_from_href(href):
"""Return the id or uuid portion of a url.
Given: 'http://www.foo.com/bar/123?q=4'
Returns: '123'
Given: 'http://www.foo.com/bar/abc123?q=4'
Returns: 'abc123'
"""
return urlparse.urlsplit("%s" % href).path.split('/')[-1]
def remove_trailing_version_from_href(href):
"""Removes the api version from the href.
Given: 'http://www.dmapi.com/compute/v1.1'
Returns: 'http://www.dmapi.com/compute'
Given: 'http://www.dmapi.com/v1.1'
Returns: 'http://www.dmapi.com'
"""
parsed_url = urlparse.urlsplit(href)
url_parts = parsed_url.path.rsplit('/', 1)
# NOTE: this should match vX.X or vX
expression = re.compile(r'^v([0-9]+|[0-9]+\.[0-9]+)(/.*|$)')
if not expression.match(url_parts.pop()):
LOG.debug('href %s does not contain version', href)
raise ValueError(_('href %s does not contain version') % href)
new_path = url_join(*url_parts)
parsed_url = list(parsed_url)
parsed_url[2] = new_path
return urlparse.urlunsplit(parsed_url)
def url_join(*parts):
"""Convenience method for joining parts of a URL
Any leading and trailing '/' characters are removed, and the parts joined
together with '/' as a separator. If last element of 'parts' is an empty
string, the returned URL will have a trailing slash.
"""
parts = parts or [""]
clean_parts = [part.strip("/") for part in parts if part]
if not parts[-1]:
# Empty last element should add a trailing slash
clean_parts.append("")
return "/".join(clean_parts)
class ViewBuilder(object):
"""Model API responses as dictionaries."""
def _get_project_id(self, request):
"""Get project id from request url if present or empty string
otherwise
"""
project_id = request.environ["dmapi.context"].project_id
if project_id and project_id in request.url:
return project_id
return ''
def _get_links(self, request, identifier, collection_name):
return [{
"rel": "self",
"href": self._get_href_link(request, identifier, collection_name),
},
{
"rel": "bookmark",
"href": self._get_bookmark_link(request,
identifier,
collection_name),
}]
def _get_next_link(self, request, identifier, collection_name):
"""Return href string with proper limit and marker params."""
params = collections.OrderedDict(sorted(request.params.items()))
params["marker"] = identifier
prefix = self._update_dmapi_link_prefix(request.application_url)
url = url_join(prefix,
self._get_project_id(request),
collection_name)
return "%s?%s" % (url, urlparse.urlencode(params))
def _get_href_link(self, request, identifier, collection_name):
"""Return an href string pointing to this object."""
prefix = self._update_dmapi_link_prefix(request.application_url)
return url_join(prefix,
self._get_project_id(request),
collection_name,
str(identifier))
def _get_bookmark_link(self, request, identifier, collection_name):
"""Create a URL that refers to a specific resource."""
base_url = remove_trailing_version_from_href(request.application_url)
base_url = self._update_dmapi_link_prefix(base_url)
return url_join(base_url,
self._get_project_id(request),
collection_name,
str(identifier))
def _get_collection_links(self,
request,
items,
collection_name,
id_key="uuid"):
"""Retrieve 'next' link, if applicable. This is included if:
1) 'limit' param is specified and equals the number of items.
2) 'limit' param is specified but it exceeds CONF.api.max_limit,
in this case the number of items is CONF.api.max_limit.
3) 'limit' param is NOT specified but the number of items is
CONF.api.max_limit.
"""
links = []
max_items = min(
int(request.params.get("limit", CONF.api.max_limit)),
CONF.api.max_limit)
if max_items and max_items == len(items):
last_item = items[-1]
if id_key in last_item:
last_item_id = last_item[id_key]
elif 'id' in last_item:
last_item_id = last_item["id"]
else:
last_item_id = last_item["flavorid"]
links.append({
"rel": "next",
"href": self._get_next_link(request,
last_item_id,
collection_name),
})
return links
def _update_link_prefix(self, orig_url, prefix):
if not prefix:
return orig_url
url_parts = list(urlparse.urlsplit(orig_url))
prefix_parts = list(urlparse.urlsplit(prefix))
url_parts[0:2] = prefix_parts[0:2]
url_parts[2] = prefix_parts[2] + url_parts[2]
return urlparse.urlunsplit(url_parts).rstrip('/')
def _update_dmapi_link_prefix(self, orig_url):
return self._update_link_prefix(orig_url, CONF.api.dmapi_link_prefix)
def get_instance(compute_api, context, instance_id, expected_attrs=None):
"""Fetch an instance from the compute API, handling error checking."""
try:
return compute_api.get(context, instance_id,
expected_attrs=expected_attrs)
except exception.InstanceNotFound as e:
raise exc.HTTPNotFound(explanation=e.format_message())
def normalize_name(name):
# NOTE(alex_xu): This method is used by v2.1 legacy v2 compat mode.
# In the legacy v2 API, some of APIs strip the spaces and some of APIs not.
# The v2.1 disallow leading/trailing, for compatible v2 API and consistent,
# we enable leading/trailing spaces and strip spaces in legacy v2 compat
# mode. Althrough in legacy v2 API there are some APIs didn't strip spaces,
# but actually leading/trailing spaces(that means user depend on leading/
# trailing spaces distinguish different instance) is pointless usecase.
return name.strip()
def raise_feature_not_supported(msg=None):
if msg is None:
msg = _("The requested functionality is not supported.")
raise webob.exc.HTTPNotImplemented(explanation=msg)