Merge "RequestDetail"
This commit is contained in:
commit
da9fad5180
|
@ -34,9 +34,10 @@ def load_extensions():
|
||||||
def get_matched_extensions(request):
|
def get_matched_extensions(request):
|
||||||
"""Return list of matched extensions for request
|
"""Return list of matched extensions for request
|
||||||
|
|
||||||
:type request: Dict[]
|
:type request: mixmatch.proxy.RequestDetails
|
||||||
:rtype: List[mixmatch.extend.base.Extension]
|
:rtype: List[mixmatch.extend.base.Extension]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _match(e):
|
def _match(e):
|
||||||
return e.obj if e.obj.matches(request) else None
|
return e.obj if e.obj.matches(request) else None
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ class Route(object):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def match(self, request):
|
def match(self, request):
|
||||||
return (self._match_service(request['service']) and
|
return (self._match_service(request.service) and
|
||||||
self._match_version(request['version']) and
|
self._match_version(request.version) and
|
||||||
self._match_method(request['method']) and
|
self._match_method(request.method) and
|
||||||
self._match_action(request['action']))
|
self._match_action(request.action))
|
||||||
|
|
|
@ -32,18 +32,18 @@ class NameRouting(base.Extension):
|
||||||
return 'MM-SERVICE-PROVIDER' in headers
|
return 'MM-SERVICE-PROVIDER' in headers
|
||||||
|
|
||||||
def handle_request(self, request):
|
def handle_request(self, request):
|
||||||
if self._is_targeted(request['headers']):
|
if self._is_targeted(request.headers):
|
||||||
return
|
return
|
||||||
|
|
||||||
body = jsonutils.loads(mm_request.data)
|
body = jsonutils.loads(mm_request.data)
|
||||||
if request['service'] == 'image':
|
if request.service == 'image':
|
||||||
if request['version'] == 'v1':
|
if request.version == 'v1':
|
||||||
name = request['headers'].get('X-IMAGE-META-NAME', '')
|
name = request.headers.get('X-IMAGE-META-NAME', '')
|
||||||
else:
|
else:
|
||||||
name = body.get('name', '')
|
name = body.get('name', '')
|
||||||
elif request['service'] == 'volume':
|
elif request.service == 'volume':
|
||||||
name = body['volume'].get('name', '')
|
name = body['volume'].get('name', '')
|
||||||
|
|
||||||
name = name.split('@')
|
name = name.split('@')
|
||||||
if len(name) == 2:
|
if len(name) == 2:
|
||||||
request['headers']['MM-SERVICE-PROVIDER'] = name[1]
|
request.headers['MM-SERVICE-PROVIDER'] = name[1]
|
||||||
|
|
|
@ -52,25 +52,6 @@ def get_service(a):
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
def get_details(method, orig_path, headers):
|
|
||||||
"""Get details for a request."""
|
|
||||||
path = orig_path.split('/')
|
|
||||||
# NOTE(knikolla): Request usually look like:
|
|
||||||
# /<service>/<version>/<project_id:uuid>/<res_type>/<res_id:uuid>
|
|
||||||
# or
|
|
||||||
# /<service>/<version>/<res_type>/<specific action>
|
|
||||||
return {'method': method,
|
|
||||||
'service': get_service(path),
|
|
||||||
'version': utils.safe_pop(path),
|
|
||||||
'project_id': utils.pop_if_uuid(path),
|
|
||||||
'action': path[:], # NOTE(knikolla): This includes
|
|
||||||
'resource_type': utils.safe_pop(path), # this
|
|
||||||
'resource_id': utils.pop_if_uuid(path), # and this
|
|
||||||
'token': headers.get('X-AUTH-TOKEN', None),
|
|
||||||
'headers': dict(headers),
|
|
||||||
'path': orig_path}
|
|
||||||
|
|
||||||
|
|
||||||
def is_json_response(response):
|
def is_json_response(response):
|
||||||
return response.headers.get('Content-Type') == 'application/json'
|
return response.headers.get('Content-Type') == 'application/json'
|
||||||
|
|
||||||
|
@ -96,14 +77,33 @@ def format_for_log(title=None, method=None, url=None, headers=None,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class RequestDetails(object):
|
||||||
|
def __init__(self, method, orig_path, headers):
|
||||||
|
local_path = orig_path.split('/')
|
||||||
|
# NOTE(knikolla): Request usually looks like this:
|
||||||
|
# /<service>/<version>/<project_id:uuid>/<res_type>/<res_id:uuid>
|
||||||
|
# or
|
||||||
|
# /<service>/<version>/<res_type>/<specific action>
|
||||||
|
self.method = method
|
||||||
|
self.service = get_service(local_path)
|
||||||
|
self.version = utils.safe_pop(local_path)
|
||||||
|
self.project_id = utils.pop_if_uuid(local_path)
|
||||||
|
self.action = local_path[:] # NOTE(knikolla): This includes
|
||||||
|
self.resource_type = utils.safe_pop(local_path) # this
|
||||||
|
self.resource_id = utils.pop_if_uuid(local_path) # and this
|
||||||
|
self.token = headers.get('X-AUTH-TOKEN', None)
|
||||||
|
self.headers = dict(headers)
|
||||||
|
self.path = orig_path
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(object):
|
class RequestHandler(object):
|
||||||
|
|
||||||
def __init__(self, method, path, headers):
|
def __init__(self, method, path, headers):
|
||||||
self.details = get_details(method, path, headers)
|
self.details = RequestDetails(method, path, headers)
|
||||||
self.extensions = extend.get_matched_extensions(self.details)
|
self.extensions = extend.get_matched_extensions(self.details)
|
||||||
self._set_strip_details(self.details)
|
self._set_strip_details(self.details)
|
||||||
self.enabled_sps = filter(
|
self.enabled_sps = filter(
|
||||||
lambda sp: (self.details['service'] in
|
lambda sp: (self.details.service in
|
||||||
service_providers.get(CONF, sp).enabled_services),
|
service_providers.get(CONF, sp).enabled_services),
|
||||||
CONF.service_providers
|
CONF.service_providers
|
||||||
)
|
)
|
||||||
|
@ -111,7 +111,7 @@ class RequestHandler(object):
|
||||||
for extension in self.extensions:
|
for extension in self.extensions:
|
||||||
extension.handle_request(self.details)
|
extension.handle_request(self.details)
|
||||||
|
|
||||||
if not self.details['version']:
|
if not self.details.version:
|
||||||
if CONF.aggregation:
|
if CONF.aggregation:
|
||||||
# unversioned calls with no action
|
# unversioned calls with no action
|
||||||
self._forward = self._list_api_versions
|
self._forward = self._list_api_versions
|
||||||
|
@ -119,32 +119,32 @@ class RequestHandler(object):
|
||||||
self._forward = self._local_forward
|
self._forward = self._local_forward
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.details['resource_type']:
|
if not self.details.resource_type:
|
||||||
# versioned calls with no action
|
# versioned calls with no action
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
mapping = None
|
mapping = None
|
||||||
|
|
||||||
if self.details['resource_id']:
|
if self.details.resource_id:
|
||||||
mapping = model.ResourceMapping.find(
|
mapping = model.ResourceMapping.find(
|
||||||
resource_type=self.details['action'][0],
|
resource_type=self.details.action[0],
|
||||||
resource_id=self.details['resource_id'])
|
resource_id=self.details.resource_id)
|
||||||
|
|
||||||
LOG.debug('Local Token: %s ' % self.details['token'])
|
LOG.debug('Local Token: %s ' % self.details.token)
|
||||||
|
|
||||||
if 'MM-SERVICE-PROVIDER' in self.details['headers']:
|
if 'MM-SERVICE-PROVIDER' in self.details.headers:
|
||||||
# The user wants a specific service provider, use that SP.
|
# The user wants a specific service provider, use that SP.
|
||||||
# FIXME(knikolla): This isn't exercised by any unit test
|
# FIXME(knikolla): This isn't exercised by any unit test
|
||||||
(self.service_provider, self.project_id) = (
|
(self.service_provider, self.project_id) = (
|
||||||
self.details['headers']['MM-SERVICE-PROVIDER'],
|
self.details.headers['MM-SERVICE-PROVIDER'],
|
||||||
self.details['headers'].get('MM-PROJECT-ID', None)
|
self.details.headers.get('MM-PROJECT-ID', None)
|
||||||
)
|
)
|
||||||
if self.service_provider not in self.enabled_sps:
|
if self.service_provider not in self.enabled_sps:
|
||||||
abort(400)
|
abort(400)
|
||||||
if not self.project_id and self.service_provider != 'default':
|
if not self.project_id and self.service_provider != 'default':
|
||||||
self.project_id = auth.get_projects_at_sp(
|
self.project_id = auth.get_projects_at_sp(
|
||||||
self.service_provider,
|
self.service_provider,
|
||||||
self.details['token']
|
self.details.token
|
||||||
)[0]
|
)[0]
|
||||||
self._forward = self._targeted_forward
|
self._forward = self._targeted_forward
|
||||||
elif mapping:
|
elif mapping:
|
||||||
|
@ -156,19 +156,19 @@ class RequestHandler(object):
|
||||||
self._forward = self._forward
|
self._forward = self._forward
|
||||||
|
|
||||||
LOG.info(format_for_log(title="Request to proxy",
|
LOG.info(format_for_log(title="Request to proxy",
|
||||||
method=self.details['method'],
|
method=self.details.method,
|
||||||
url=self.details['path'],
|
url=self.details.path,
|
||||||
headers=dict(self.details['headers'])))
|
headers=dict(self.details.headers)))
|
||||||
|
|
||||||
def _do_request_on(self, sp, project_id=None):
|
def _do_request_on(self, sp, project_id=None):
|
||||||
headers = self._prepare_headers(self.details['headers'])
|
headers = self._prepare_headers(self.details.headers)
|
||||||
|
|
||||||
if self.details['token']:
|
if self.details.token:
|
||||||
if sp == 'default':
|
if sp == 'default':
|
||||||
auth_session = auth.get_local_auth(self.details['token'])
|
auth_session = auth.get_local_auth(self.details.token)
|
||||||
else:
|
else:
|
||||||
auth_session = auth.get_sp_auth(sp,
|
auth_session = auth.get_sp_auth(sp,
|
||||||
self.details['token'],
|
self.details.token,
|
||||||
project_id)
|
project_id)
|
||||||
headers['X-AUTH-TOKEN'] = auth_session.get_token()
|
headers['X-AUTH-TOKEN'] = auth_session.get_token()
|
||||||
project_id = auth_session.get_project_id()
|
project_id = auth_session.get_project_id()
|
||||||
|
@ -177,14 +177,14 @@ class RequestHandler(object):
|
||||||
|
|
||||||
url = services.construct_url(
|
url = services.construct_url(
|
||||||
sp,
|
sp,
|
||||||
self.details['service'],
|
self.details.service,
|
||||||
self.details['version'],
|
self.details.version,
|
||||||
self.details['action'],
|
self.details.action,
|
||||||
project_id=project_id
|
project_id=project_id
|
||||||
)
|
)
|
||||||
|
|
||||||
request_kwargs = {
|
request_kwargs = {
|
||||||
'method': self.details['method'],
|
'method': self.details.method,
|
||||||
'url': url,
|
'url': url,
|
||||||
'headers': headers,
|
'headers': headers,
|
||||||
'params': self._prepare_args(request.args)
|
'params': self._prepare_args(request.args)
|
||||||
|
@ -197,7 +197,7 @@ class RequestHandler(object):
|
||||||
stream=self.stream,
|
stream=self.stream,
|
||||||
**request_kwargs)
|
**request_kwargs)
|
||||||
LOG.info(format_for_log(title='Request from proxy',
|
LOG.info(format_for_log(title='Request from proxy',
|
||||||
method=self.details['method'],
|
method=self.details.method,
|
||||||
url=url,
|
url=url,
|
||||||
headers=headers))
|
headers=headers))
|
||||||
LOG.info(format_for_log(title='Response to proxy',
|
LOG.info(format_for_log(title='Response to proxy',
|
||||||
|
@ -246,7 +246,7 @@ class RequestHandler(object):
|
||||||
else:
|
else:
|
||||||
errors[r.status_code].append(r)
|
errors[r.status_code].append(r)
|
||||||
else:
|
else:
|
||||||
for p in auth.get_projects_at_sp(sp, self.details['token']):
|
for p in auth.get_projects_at_sp(sp, self.details.token):
|
||||||
r = self._do_request_on(sp, p)
|
r = self._do_request_on(sp, p)
|
||||||
if 200 <= r.status_code < 300:
|
if 200 <= r.status_code < 300:
|
||||||
responses[(sp, p)] = r
|
responses[(sp, p)] = r
|
||||||
|
@ -262,9 +262,9 @@ class RequestHandler(object):
|
||||||
# for everything that is returned.
|
# for everything that is returned.
|
||||||
return flask.Response(
|
return flask.Response(
|
||||||
services.aggregate(responses,
|
services.aggregate(responses,
|
||||||
self.details['action'][0],
|
self.details.action[0],
|
||||||
self.details['service'],
|
self.details.service,
|
||||||
version=self.details['version'],
|
version=self.details.version,
|
||||||
params=dict(request.args),
|
params=dict(request.args),
|
||||||
path=request.base_url,
|
path=request.base_url,
|
||||||
strip_details=self.strip_details),
|
strip_details=self.strip_details),
|
||||||
|
@ -284,7 +284,7 @@ class RequestHandler(object):
|
||||||
return flask.Response("Something strange happened.\n", 500)
|
return flask.Response("Something strange happened.\n", 500)
|
||||||
|
|
||||||
def _list_api_versions(self):
|
def _list_api_versions(self):
|
||||||
return services.list_api_versions(self.details['service'],
|
return services.list_api_versions(self.details.service,
|
||||||
request.base_url)
|
request.base_url)
|
||||||
|
|
||||||
def forward(self):
|
def forward(self):
|
||||||
|
@ -318,12 +318,12 @@ class RequestHandler(object):
|
||||||
|
|
||||||
@utils.CachedProperty
|
@utils.CachedProperty
|
||||||
def chunked(self):
|
def chunked(self):
|
||||||
encoding = self.details['headers'].get('Transfer-Encoding', '')
|
encoding = self.details.headers.get('Transfer-Encoding', '')
|
||||||
return encoding.lower() == 'chunked'
|
return encoding.lower() == 'chunked'
|
||||||
|
|
||||||
@utils.CachedProperty
|
@utils.CachedProperty
|
||||||
def stream(self):
|
def stream(self):
|
||||||
return self.details['method'] == 'GET'
|
return self.details.method == 'GET'
|
||||||
|
|
||||||
@utils.CachedProperty
|
@utils.CachedProperty
|
||||||
def fallback_to_local(self):
|
def fallback_to_local(self):
|
||||||
|
@ -334,9 +334,9 @@ class RequestHandler(object):
|
||||||
@utils.CachedProperty
|
@utils.CachedProperty
|
||||||
def aggregate(self):
|
def aggregate(self):
|
||||||
"""Return true if this is a case where we should aggregate."""
|
"""Return true if this is a case where we should aggregate."""
|
||||||
return (not self.details['resource_id'] and
|
return (not self.details.resource_id and
|
||||||
self.details['method'] == 'GET' and
|
self.details.method == 'GET' and
|
||||||
self.details['action'][0] in RESOURCES_AGGREGATE)
|
self.details.action[0] in RESOURCES_AGGREGATE)
|
||||||
|
|
||||||
@utils.CachedProperty
|
@utils.CachedProperty
|
||||||
def session(self):
|
def session(self):
|
||||||
|
@ -356,11 +356,11 @@ class RequestHandler(object):
|
||||||
# if request is to /volumes, change it
|
# if request is to /volumes, change it
|
||||||
# to /volumes/detail for aggregation
|
# to /volumes/detail for aggregation
|
||||||
# and set strip_details to true
|
# and set strip_details to true
|
||||||
if (details['service'] == 'volume' and
|
if (details.service == 'volume' and
|
||||||
details['method'] == 'GET' and
|
details.method == 'GET' and
|
||||||
utils.safe_get(details['action'], -1) == 'volumes'):
|
utils.safe_get(details.action, -1) == 'volumes'):
|
||||||
self.strip_details = True
|
self.strip_details = True
|
||||||
details['action'].insert(len(details['action']), 'detail')
|
details.action.insert(len(details.action), 'detail')
|
||||||
else:
|
else:
|
||||||
self.strip_details = False
|
self.strip_details = False
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue