summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2017-08-16 14:28:50 +0000
committerGerrit Code Review <review@openstack.org>2017-08-16 14:28:50 +0000
commitda9fad51807f50bbb1fa2131933bb4b5bb04d460 (patch)
treea3232b6311de590a34f91817bd064bfcacc03739
parentcbbef62c50da5c8115f2da98e67ad4aeaa495a7f (diff)
parent06543ccbedb34ba76865427c527526b5035c4a82 (diff)
Merge "RequestDetail"
-rw-r--r--mixmatch/extend/__init__.py3
-rw-r--r--mixmatch/extend/base.py8
-rw-r--r--mixmatch/extend/name_routing.py12
-rw-r--r--mixmatch/proxy.py114
4 files changed, 69 insertions, 68 deletions
diff --git a/mixmatch/extend/__init__.py b/mixmatch/extend/__init__.py
index 55fdea8..8e039f1 100644
--- a/mixmatch/extend/__init__.py
+++ b/mixmatch/extend/__init__.py
@@ -34,9 +34,10 @@ def load_extensions():
34def get_matched_extensions(request): 34def get_matched_extensions(request):
35 """Return list of matched extensions for request 35 """Return list of matched extensions for request
36 36
37 :type request: Dict[] 37 :type request: mixmatch.proxy.RequestDetails
38 :rtype: List[mixmatch.extend.base.Extension] 38 :rtype: List[mixmatch.extend.base.Extension]
39 """ 39 """
40
40 def _match(e): 41 def _match(e):
41 return e.obj if e.obj.matches(request) else None 42 return e.obj if e.obj.matches(request) else None
42 43
diff --git a/mixmatch/extend/base.py b/mixmatch/extend/base.py
index ff11bea..5185aff 100644
--- a/mixmatch/extend/base.py
+++ b/mixmatch/extend/base.py
@@ -59,7 +59,7 @@ class Route(object):
59 return True 59 return True
60 60
61 def match(self, request): 61 def match(self, request):
62 return (self._match_service(request['service']) and 62 return (self._match_service(request.service) and
63 self._match_version(request['version']) and 63 self._match_version(request.version) and
64 self._match_method(request['method']) and 64 self._match_method(request.method) and
65 self._match_action(request['action'])) 65 self._match_action(request.action))
diff --git a/mixmatch/extend/name_routing.py b/mixmatch/extend/name_routing.py
index 170c3fb..67f11de 100644
--- a/mixmatch/extend/name_routing.py
+++ b/mixmatch/extend/name_routing.py
@@ -32,18 +32,18 @@ class NameRouting(base.Extension):
32 return 'MM-SERVICE-PROVIDER' in headers 32 return 'MM-SERVICE-PROVIDER' in headers
33 33
34 def handle_request(self, request): 34 def handle_request(self, request):
35 if self._is_targeted(request['headers']): 35 if self._is_targeted(request.headers):
36 return 36 return
37 37
38 body = jsonutils.loads(mm_request.data) 38 body = jsonutils.loads(mm_request.data)
39 if request['service'] == 'image': 39 if request.service == 'image':
40 if request['version'] == 'v1': 40 if request.version == 'v1':
41 name = request['headers'].get('X-IMAGE-META-NAME', '') 41 name = request.headers.get('X-IMAGE-META-NAME', '')
42 else: 42 else:
43 name = body.get('name', '') 43 name = body.get('name', '')
44 elif request['service'] == 'volume': 44 elif request.service == 'volume':
45 name = body['volume'].get('name', '') 45 name = body['volume'].get('name', '')
46 46
47 name = name.split('@') 47 name = name.split('@')
48 if len(name) == 2: 48 if len(name) == 2:
49 request['headers']['MM-SERVICE-PROVIDER'] = name[1] 49 request.headers['MM-SERVICE-PROVIDER'] = name[1]
diff --git a/mixmatch/proxy.py b/mixmatch/proxy.py
index 0c5b09b..fd25522 100644
--- a/mixmatch/proxy.py
+++ b/mixmatch/proxy.py
@@ -52,25 +52,6 @@ def get_service(a):
52 abort(404) 52 abort(404)
53 53
54 54
55def get_details(method, orig_path, headers):
56 """Get details for a request."""
57 path = orig_path.split('/')
58 # NOTE(knikolla): Request usually look like:
59 # /<service>/<version>/<project_id:uuid>/<res_type>/<res_id:uuid>
60 # or
61 # /<service>/<version>/<res_type>/<specific action>
62 return {'method': method,
63 'service': get_service(path),
64 'version': utils.safe_pop(path),
65 'project_id': utils.pop_if_uuid(path),
66 'action': path[:], # NOTE(knikolla): This includes
67 'resource_type': utils.safe_pop(path), # this
68 'resource_id': utils.pop_if_uuid(path), # and this
69 'token': headers.get('X-AUTH-TOKEN', None),
70 'headers': dict(headers),
71 'path': orig_path}
72
73
74def is_json_response(response): 55def is_json_response(response):
75 return response.headers.get('Content-Type') == 'application/json' 56 return response.headers.get('Content-Type') == 'application/json'
76 57
@@ -96,14 +77,33 @@ def format_for_log(title=None, method=None, url=None, headers=None,
96 ]) 77 ])
97 78
98 79
80class RequestDetails(object):
81 def __init__(self, method, orig_path, headers):
82 local_path = orig_path.split('/')
83 # NOTE(knikolla): Request usually looks like this:
84 # /<service>/<version>/<project_id:uuid>/<res_type>/<res_id:uuid>
85 # or
86 # /<service>/<version>/<res_type>/<specific action>
87 self.method = method
88 self.service = get_service(local_path)
89 self.version = utils.safe_pop(local_path)
90 self.project_id = utils.pop_if_uuid(local_path)
91 self.action = local_path[:] # NOTE(knikolla): This includes
92 self.resource_type = utils.safe_pop(local_path) # this
93 self.resource_id = utils.pop_if_uuid(local_path) # and this
94 self.token = headers.get('X-AUTH-TOKEN', None)
95 self.headers = dict(headers)
96 self.path = orig_path
97
98
99class RequestHandler(object): 99class RequestHandler(object):
100 100
101 def __init__(self, method, path, headers): 101 def __init__(self, method, path, headers):
102 self.details = get_details(method, path, headers) 102 self.details = RequestDetails(method, path, headers)
103 self.extensions = extend.get_matched_extensions(self.details) 103 self.extensions = extend.get_matched_extensions(self.details)
104 self._set_strip_details(self.details) 104 self._set_strip_details(self.details)
105 self.enabled_sps = filter( 105 self.enabled_sps = filter(
106 lambda sp: (self.details['service'] in 106 lambda sp: (self.details.service in
107 service_providers.get(CONF, sp).enabled_services), 107 service_providers.get(CONF, sp).enabled_services),
108 CONF.service_providers 108 CONF.service_providers
109 ) 109 )
@@ -111,7 +111,7 @@ class RequestHandler(object):
111 for extension in self.extensions: 111 for extension in self.extensions:
112 extension.handle_request(self.details) 112 extension.handle_request(self.details)
113 113
114 if not self.details['version']: 114 if not self.details.version:
115 if CONF.aggregation: 115 if CONF.aggregation:
116 # unversioned calls with no action 116 # unversioned calls with no action
117 self._forward = self._list_api_versions 117 self._forward = self._list_api_versions
@@ -119,32 +119,32 @@ class RequestHandler(object):
119 self._forward = self._local_forward 119 self._forward = self._local_forward
120 return 120 return
121 121
122 if not self.details['resource_type']: 122 if not self.details.resource_type:
123 # versioned calls with no action 123 # versioned calls with no action
124 abort(400) 124 abort(400)
125 125
126 mapping = None 126 mapping = None
127 127
128 if self.details['resource_id']: 128 if self.details.resource_id:
129 mapping = model.ResourceMapping.find( 129 mapping = model.ResourceMapping.find(
130 resource_type=self.details['action'][0], 130 resource_type=self.details.action[0],
131 resource_id=self.details['resource_id']) 131 resource_id=self.details.resource_id)
132 132
133 LOG.debug('Local Token: %s ' % self.details['token']) 133 LOG.debug('Local Token: %s ' % self.details.token)
134 134
135 if 'MM-SERVICE-PROVIDER' in self.details['headers']: 135 if 'MM-SERVICE-PROVIDER' in self.details.headers:
136 # The user wants a specific service provider, use that SP. 136 # The user wants a specific service provider, use that SP.
137 # FIXME(knikolla): This isn't exercised by any unit test 137 # FIXME(knikolla): This isn't exercised by any unit test
138 (self.service_provider, self.project_id) = ( 138 (self.service_provider, self.project_id) = (
139 self.details['headers']['MM-SERVICE-PROVIDER'], 139 self.details.headers['MM-SERVICE-PROVIDER'],
140 self.details['headers'].get('MM-PROJECT-ID', None) 140 self.details.headers.get('MM-PROJECT-ID', None)
141 ) 141 )
142 if self.service_provider not in self.enabled_sps: 142 if self.service_provider not in self.enabled_sps:
143 abort(400) 143 abort(400)
144 if not self.project_id and self.service_provider != 'default': 144 if not self.project_id and self.service_provider != 'default':
145 self.project_id = auth.get_projects_at_sp( 145 self.project_id = auth.get_projects_at_sp(
146 self.service_provider, 146 self.service_provider,
147 self.details['token'] 147 self.details.token
148 )[0] 148 )[0]
149 self._forward = self._targeted_forward 149 self._forward = self._targeted_forward
150 elif mapping: 150 elif mapping:
@@ -156,19 +156,19 @@ class RequestHandler(object):
156 self._forward = self._forward 156 self._forward = self._forward
157 157
158 LOG.info(format_for_log(title="Request to proxy", 158 LOG.info(format_for_log(title="Request to proxy",
159 method=self.details['method'], 159 method=self.details.method,
160 url=self.details['path'], 160 url=self.details.path,
161 headers=dict(self.details['headers']))) 161 headers=dict(self.details.headers)))
162 162
163 def _do_request_on(self, sp, project_id=None): 163 def _do_request_on(self, sp, project_id=None):
164 headers = self._prepare_headers(self.details['headers']) 164 headers = self._prepare_headers(self.details.headers)
165 165
166 if self.details['token']: 166 if self.details.token:
167 if sp == 'default': 167 if sp == 'default':
168 auth_session = auth.get_local_auth(self.details['token']) 168 auth_session = auth.get_local_auth(self.details.token)
169 else: 169 else:
170 auth_session = auth.get_sp_auth(sp, 170 auth_session = auth.get_sp_auth(sp,
171 self.details['token'], 171 self.details.token,
172 project_id) 172 project_id)
173 headers['X-AUTH-TOKEN'] = auth_session.get_token() 173 headers['X-AUTH-TOKEN'] = auth_session.get_token()
174 project_id = auth_session.get_project_id() 174 project_id = auth_session.get_project_id()
@@ -177,14 +177,14 @@ class RequestHandler(object):
177 177
178 url = services.construct_url( 178 url = services.construct_url(
179 sp, 179 sp,
180 self.details['service'], 180 self.details.service,
181 self.details['version'], 181 self.details.version,
182 self.details['action'], 182 self.details.action,
183 project_id=project_id 183 project_id=project_id
184 ) 184 )
185 185
186 request_kwargs = { 186 request_kwargs = {
187 'method': self.details['method'], 187 'method': self.details.method,
188 'url': url, 188 'url': url,
189 'headers': headers, 189 'headers': headers,
190 'params': self._prepare_args(request.args) 190 'params': self._prepare_args(request.args)
@@ -197,7 +197,7 @@ class RequestHandler(object):
197 stream=self.stream, 197 stream=self.stream,
198 **request_kwargs) 198 **request_kwargs)
199 LOG.info(format_for_log(title='Request from proxy', 199 LOG.info(format_for_log(title='Request from proxy',
200 method=self.details['method'], 200 method=self.details.method,
201 url=url, 201 url=url,
202 headers=headers)) 202 headers=headers))
203 LOG.info(format_for_log(title='Response to proxy', 203 LOG.info(format_for_log(title='Response to proxy',
@@ -246,7 +246,7 @@ class RequestHandler(object):
246 else: 246 else:
247 errors[r.status_code].append(r) 247 errors[r.status_code].append(r)
248 else: 248 else:
249 for p in auth.get_projects_at_sp(sp, self.details['token']): 249 for p in auth.get_projects_at_sp(sp, self.details.token):
250 r = self._do_request_on(sp, p) 250 r = self._do_request_on(sp, p)
251 if 200 <= r.status_code < 300: 251 if 200 <= r.status_code < 300:
252 responses[(sp, p)] = r 252 responses[(sp, p)] = r
@@ -262,9 +262,9 @@ class RequestHandler(object):
262 # for everything that is returned. 262 # for everything that is returned.
263 return flask.Response( 263 return flask.Response(
264 services.aggregate(responses, 264 services.aggregate(responses,
265 self.details['action'][0], 265 self.details.action[0],
266 self.details['service'], 266 self.details.service,
267 version=self.details['version'], 267 version=self.details.version,
268 params=dict(request.args), 268 params=dict(request.args),
269 path=request.base_url, 269 path=request.base_url,
270 strip_details=self.strip_details), 270 strip_details=self.strip_details),
@@ -284,7 +284,7 @@ class RequestHandler(object):
284 return flask.Response("Something strange happened.\n", 500) 284 return flask.Response("Something strange happened.\n", 500)
285 285
286 def _list_api_versions(self): 286 def _list_api_versions(self):
287 return services.list_api_versions(self.details['service'], 287 return services.list_api_versions(self.details.service,
288 request.base_url) 288 request.base_url)
289 289
290 def forward(self): 290 def forward(self):
@@ -318,12 +318,12 @@ class RequestHandler(object):
318 318
319 @utils.CachedProperty 319 @utils.CachedProperty
320 def chunked(self): 320 def chunked(self):
321 encoding = self.details['headers'].get('Transfer-Encoding', '') 321 encoding = self.details.headers.get('Transfer-Encoding', '')
322 return encoding.lower() == 'chunked' 322 return encoding.lower() == 'chunked'
323 323
324 @utils.CachedProperty 324 @utils.CachedProperty
325 def stream(self): 325 def stream(self):
326 return self.details['method'] == 'GET' 326 return self.details.method == 'GET'
327 327
328 @utils.CachedProperty 328 @utils.CachedProperty
329 def fallback_to_local(self): 329 def fallback_to_local(self):
@@ -334,9 +334,9 @@ class RequestHandler(object):
334 @utils.CachedProperty 334 @utils.CachedProperty
335 def aggregate(self): 335 def aggregate(self):
336 """Return true if this is a case where we should aggregate.""" 336 """Return true if this is a case where we should aggregate."""
337 return (not self.details['resource_id'] and 337 return (not self.details.resource_id and
338 self.details['method'] == 'GET' and 338 self.details.method == 'GET' and
339 self.details['action'][0] in RESOURCES_AGGREGATE) 339 self.details.action[0] in RESOURCES_AGGREGATE)
340 340
341 @utils.CachedProperty 341 @utils.CachedProperty
342 def session(self): 342 def session(self):
@@ -356,11 +356,11 @@ class RequestHandler(object):
356 # if request is to /volumes, change it 356 # if request is to /volumes, change it
357 # to /volumes/detail for aggregation 357 # to /volumes/detail for aggregation
358 # and set strip_details to true 358 # and set strip_details to true
359 if (details['service'] == 'volume' and 359 if (details.service == 'volume' and
360 details['method'] == 'GET' and 360 details.method == 'GET' and
361 utils.safe_get(details['action'], -1) == 'volumes'): 361 utils.safe_get(details.action, -1) == 'volumes'):
362 self.strip_details = True 362 self.strip_details = True
363 details['action'].insert(len(details['action']), 'detail') 363 details.action.insert(len(details.action), 'detail')
364 else: 364 else:
365 self.strip_details = False 365 self.strip_details = False
366 366