Merge "Combine aggregate_forward and search_forward"

This commit is contained in:
Jenkins 2017-07-21 19:52:48 +00:00 committed by Gerrit Code Review
commit 749de86ac5
2 changed files with 79 additions and 48 deletions

View File

@ -116,16 +116,11 @@ class RequestHandler(object):
abort(400)
mapping = None
aggregate = False
if self.details['resource_id']:
mapping = model.ResourceMapping.find(
resource_type=self.details['action'][0],
resource_id=self.details['resource_id'])
else:
if (self.details['method'] == 'GET' and
self.details['action'][0] in RESOURCES_AGGREGATE):
aggregate = True
LOG.debug('Local Token: %s ' % self.details['token'])
@ -142,15 +137,13 @@ class RequestHandler(object):
self.details['token']
)[0]
self._forward = self._targeted_forward
elif aggregate:
self._forward = self._aggregate_forward
elif mapping:
# Which we already know the location of, use that SP.
self.service_provider = mapping.resource_sp
self.project_id = mapping.project_id
self._forward = self._targeted_forward
else:
self._forward = self._search_forward
self._forward = self._forward
LOG.info(format_for_log(title="Request to proxy",
method=self.details['method'],
@ -198,7 +191,6 @@ class RequestHandler(object):
headers=headers))
LOG.info(format_for_log(title='Response to proxy',
status_code=resp.status_code,
url=resp.url,
headers=resp.headers))
return resp
@ -215,7 +207,6 @@ class RequestHandler(object):
)
LOG.info(format_for_log(title='Response from proxy',
status_code=final_response.status_code,
url=response.url,
headers=dict(final_response.headers)))
return final_response
@ -226,26 +217,48 @@ class RequestHandler(object):
return self._finalize(
self._do_request_on(self.service_provider, self.project_id))
def _search_forward(self):
if not CONF.search_by_broadcast:
def _forward(self):
if self.fallback_to_local:
return self._local_forward()
responses = {}
errors = collections.defaultdict(lambda: [])
for sp in self.enabled_sps:
if sp == 'default':
response = self._do_request_on('default')
if 200 <= response.status_code < 300:
return self._finalize(response)
r = self._do_request_on('default')
if 200 <= r.status_code < 300:
responses['default'] = r
if not self.aggregate:
return self._finalize(r)
else:
errors[response.status_code].append(response)
errors[r.status_code].append(r)
else:
self.service_provider = sp
for p in auth.get_projects_at_sp(sp, self.details['token']):
response = self._do_request_on(sp, p)
if 200 <= response.status_code < 300:
return self._finalize(response)
r = self._do_request_on(sp, p)
if 200 <= r.status_code < 300:
responses[(sp, p)] = r
if not self.aggregate:
return self._finalize(r)
else:
errors[response.status_code].append(response)
errors[r.status_code].append(r)
# NOTE(knikolla): If we haven't returned yet, either we're aggregating
# or there are errors.
if not errors:
# TODO(knikolla): Plug this into _finalize to have a common path
# for everything that is returned.
return flask.Response(
services.aggregate(responses,
self.details['action'][0],
self.details['service'],
version=self.details['version'],
params=request.args.to_dict(),
path=request.base_url,
strip_details=self.strip_details),
200,
content_type='application/json'
)
if six.viewkeys(errors) == {404}:
return self._finalize(errors[404][0])
@ -258,31 +271,6 @@ class RequestHandler(object):
# TODO(jfreud): log
return flask.Response("Something strange happened.\n", 500)
def _aggregate_forward(self):
if not CONF.aggregation:
return self._local_forward()
responses = {}
for sp in self.enabled_sps:
if sp == 'default':
responses['default'] = self._do_request_on('default')
else:
for proj in auth.get_projects_at_sp(sp, self.details['token']):
responses[(sp, proj)] = self._do_request_on(sp, proj)
return flask.Response(
services.aggregate(responses,
self.details['action'][0],
self.details['service'],
version=self.details['version'],
params=request.args.to_dict(),
path=request.base_url,
strip_details=self.strip_details),
200,
content_type='application/json'
)
def _list_api_versions(self):
return services.list_api_versions(self.details['service'],
request.base_url)
@ -316,15 +304,28 @@ class RequestHandler(object):
args.pop('marker', None)
return args
@property
@utils.CachedProperty
def chunked(self):
encoding = self.details['headers'].get('Transfer-Encoding', '')
return encoding.lower() == 'chunked'
@property
@utils.CachedProperty
def stream(self):
return True if self.details['method'] in ['GET'] else False
@utils.CachedProperty
def fallback_to_local(self):
"""Return true if can't aggregate or search."""
return ((self.aggregate and not CONF.aggregation) or
(not self.aggregate and not CONF.search_by_broadcast))
@utils.CachedProperty
def aggregate(self):
"""Return true if this is a case where we should aggregate."""
return (not self.details['resource_id'] and
self.details['method'] == 'GET' and
self.details['action'][0] in RESOURCES_AGGREGATE)
def _set_strip_details(self, details):
# if request is to /volumes, change it
# to /volumes/detail for aggregation

View File

@ -15,6 +15,36 @@
import uuid
class CachedProperty(object):
"""A decorator that converts a function into a lazy property.
Taken from : https://github.com/nshah/python-memoize
The function wrapped is called the first time to retrieve the result
and then that calculated result is used the next time you access
the value:
class Foo(object):
@CachedProperty
def bar(self):
# calculate something important here
return 42
"""
def __init__(self, func, name=None, doc=None):
self.func = func
self.__name__ = name or func.__name__
self.__doc__ = doc or func.__doc__
def __get__(self, obj, owner):
if obj is None:
return self
value = self.func(obj)
setattr(obj, self.__name__, value)
return value
def safe_get(a, i, default=None):
"""Return the i-th element if it exists, or default."""
try: