Add request id to returned objects

Adding two classes RequestIdProxy and GeneratorProxy derived from
wrapt.ObjectProxy to wrap objects returned from the API.

GeneratorProxy class is used to wrap generator objects returned
by cases like images.list() etc. whereas RequestIdProxy class is
used to wrap non-generator object cases like images.create() etc.

In all cases the returned object will have the same behavior as
the wrapped(original) object. However now the returned objects
will have an extra property 'request_ids' which is a list of
exactly one request id.

For generator cases the request_ids property will be an empty list
until the underlying generator is invoked at-least once.

Co-Authored-By: Abhishek Kekane <abhishek.kekane@nttdata.com>

Closes-Bug: #1525259
Blueprint: return-request-id-to-caller
Change-Id: If8c0e0843270ff718a37ca2697afeb8da22aa3b1
This commit is contained in:
Ravi Jethani
2016-04-22 13:54:49 +00:00
committed by Abhishek Kekane
parent 54e6faadf2
commit 610177a779
20 changed files with 578 additions and 211 deletions
+109 -41
View File
@@ -37,6 +37,7 @@ class NamespaceController(object):
return warlock.model_factory(schema.raw(),
base_class=schemas.SchemaBasedModel)
@utils.add_req_id_to_object()
def create(self, **kwargs):
"""Create a namespace.
@@ -50,7 +51,7 @@ class NamespaceController(object):
resp, body = self.http_client.post(url, data=namespace)
body.pop('self', None)
return self.model(**body)
return self.model(**body), resp
def update(self, namespace_name, **kwargs):
"""Update a namespace.
@@ -72,23 +73,34 @@ class NamespaceController(object):
del namespace[elem]
url = '/v2/metadefs/namespaces/{0}'.format(namespace_name)
self.http_client.put(url, data=namespace)
return self.get(namespace.namespace)
# Pass the original wrapped value to http client.
resp, _ = self.http_client.put(url, data=namespace.wrapped)
# Get request id from `put` request so it can be passed to the
# following `get` call
req_id_hdr = {
'x-openstack-request-id': utils._extract_request_id(resp)
}
return self._get(namespace.namespace, header=req_id_hdr)
def get(self, namespace, **kwargs):
return self._get(namespace, **kwargs)
@utils.add_req_id_to_object()
def _get(self, namespace, header=None, **kwargs):
"""Get one namespace."""
query_params = parse.urlencode(kwargs)
if kwargs:
query_params = '?%s' % query_params
url = '/v2/metadefs/namespaces/{0}{1}'.format(namespace, query_params)
resp, body = self.http_client.get(url)
header = header or {}
resp, body = self.http_client.get(url, headers=header)
# NOTE(bcwaldon): remove 'self' for now until we have an elegant
# way to pass it into the model constructor without conflict
body.pop('self', None)
return self.model(**body)
return self.model(**body), resp
@utils.add_req_id_to_generator()
def list(self, **kwargs):
"""Retrieve a listing of Namespace objects.
@@ -117,7 +129,7 @@ class NamespaceController(object):
# an elegant way to pass it into the model constructor
# without conflict.
namespace.pop('self', None)
yield self.model(**namespace)
yield self.model(**namespace), resp
# NOTE(zhiyan): In order to resolve the performance issue
# of JSON schema validation for image listing case, we
# don't validate each image entry but do it only on first
@@ -132,8 +144,8 @@ class NamespaceController(object):
except KeyError:
return
else:
for namespace in paginate(next_url):
yield namespace
for namespace, resp in paginate(next_url):
yield namespace, resp
filters = kwargs.get('filters', {})
filters = {} if filters is None else filters
@@ -170,13 +182,15 @@ class NamespaceController(object):
url = '/v2/metadefs/namespaces?%s' % parse.urlencode(filters)
for namespace in paginate(url):
yield namespace
for namespace, resp in paginate(url):
yield namespace, resp
@utils.add_req_id_to_object()
def delete(self, namespace):
"""Delete a namespace."""
url = '/v2/metadefs/namespaces/{0}'.format(namespace)
self.http_client.delete(url)
resp, body = self.http_client.delete(url)
return (resp, body), resp
class ResourceTypeController(object):
@@ -190,6 +204,7 @@ class ResourceTypeController(object):
return warlock.model_factory(schema.raw(),
base_class=schemas.SchemaBasedModel)
@utils.add_req_id_to_object()
def associate(self, namespace, **kwargs):
"""Associate a resource type with a namespace."""
try:
@@ -201,14 +216,17 @@ class ResourceTypeController(object):
res_type)
resp, body = self.http_client.post(url, data=res_type)
body.pop('self', None)
return self.model(**body)
return self.model(**body), resp
@utils.add_req_id_to_object()
def deassociate(self, namespace, resource):
"""Deassociate a resource type with a namespace."""
url = '/v2/metadefs/namespaces/{0}/resource_types/{1}'. \
format(namespace, resource)
self.http_client.delete(url)
resp, body = self.http_client.delete(url)
return (resp, body), resp
@utils.add_req_id_to_generator()
def list(self):
"""Retrieve a listing of available resource types.
@@ -218,14 +236,15 @@ class ResourceTypeController(object):
url = '/v2/metadefs/resource_types'
resp, body = self.http_client.get(url)
for resource_type in body['resource_types']:
yield self.model(**resource_type)
yield self.model(**resource_type), resp
@utils.add_req_id_to_generator()
def get(self, namespace):
url = '/v2/metadefs/namespaces/{0}/resource_types'.format(namespace)
resp, body = self.http_client.get(url)
body.pop('self', None)
for resource_type in body['resource_type_associations']:
yield self.model(**resource_type)
yield self.model(**resource_type), resp
class PropertyController(object):
@@ -239,6 +258,7 @@ class PropertyController(object):
return warlock.model_factory(schema.raw(),
base_class=schemas.SchemaBasedModel)
@utils.add_req_id_to_object()
def create(self, namespace, **kwargs):
"""Create a property.
@@ -254,7 +274,7 @@ class PropertyController(object):
resp, body = self.http_client.post(url, data=prop)
body.pop('self', None)
return self.model(**body)
return self.model(**body), resp
def update(self, namespace, prop_name, **kwargs):
"""Update a property.
@@ -272,18 +292,29 @@ class PropertyController(object):
url = '/v2/metadefs/namespaces/{0}/properties/{1}'.format(namespace,
prop_name)
self.http_client.put(url, data=prop)
# Pass the original wrapped value to http client.
resp, _ = self.http_client.put(url, data=prop.wrapped)
# Get request id from `put` request so it can be passed to the
# following `get` call
req_id_hdr = {
'x-openstack-request-id': utils._extract_request_id(resp)}
return self.get(namespace, prop.name)
return self._get(namespace, prop.name, req_id_hdr)
def get(self, namespace, prop_name):
return self._get(namespace, prop_name)
@utils.add_req_id_to_object()
def _get(self, namespace, prop_name, header=None):
url = '/v2/metadefs/namespaces/{0}/properties/{1}'.format(namespace,
prop_name)
resp, body = self.http_client.get(url)
header = header or {}
resp, body = self.http_client.get(url, headers=header)
body.pop('self', None)
body['name'] = prop_name
return self.model(**body)
return self.model(**body), resp
@utils.add_req_id_to_generator()
def list(self, namespace, **kwargs):
"""Retrieve a listing of metadata properties.
@@ -295,18 +326,22 @@ class PropertyController(object):
for key, value in body['properties'].items():
value['name'] = key
yield self.model(value)
yield self.model(value), resp
@utils.add_req_id_to_object()
def delete(self, namespace, prop_name):
"""Delete a property."""
url = '/v2/metadefs/namespaces/{0}/properties/{1}'.format(namespace,
prop_name)
self.http_client.delete(url)
resp, body = self.http_client.delete(url)
return (resp, body), resp
@utils.add_req_id_to_object()
def delete_all(self, namespace):
"""Delete all properties in a namespace."""
url = '/v2/metadefs/namespaces/{0}/properties'.format(namespace)
self.http_client.delete(url)
resp, body = self.http_client.delete(url)
return (resp, body), resp
class ObjectController(object):
@@ -320,6 +355,7 @@ class ObjectController(object):
return warlock.model_factory(schema.raw(),
base_class=schemas.SchemaBasedModel)
@utils.add_req_id_to_object()
def create(self, namespace, **kwargs):
"""Create an object.
@@ -335,7 +371,7 @@ class ObjectController(object):
resp, body = self.http_client.post(url, data=obj)
body.pop('self', None)
return self.model(**body)
return self.model(**body), resp
def update(self, namespace, object_name, **kwargs):
"""Update an object.
@@ -359,17 +395,28 @@ class ObjectController(object):
url = '/v2/metadefs/namespaces/{0}/objects/{1}'.format(namespace,
object_name)
self.http_client.put(url, data=obj)
# Pass the original wrapped value to http client.
resp, _ = self.http_client.put(url, data=obj.wrapped)
# Get request id from `put` request so it can be passed to the
# following `get` call
req_id_hdr = {
'x-openstack-request-id': utils._extract_request_id(resp)}
return self.get(namespace, obj.name)
return self._get(namespace, obj.name, req_id_hdr)
def get(self, namespace, object_name):
return self._get(namespace, object_name)
@utils.add_req_id_to_object()
def _get(self, namespace, object_name, header=None):
url = '/v2/metadefs/namespaces/{0}/objects/{1}'.format(namespace,
object_name)
resp, body = self.http_client.get(url)
header = header or {}
resp, body = self.http_client.get(url, headers=header)
body.pop('self', None)
return self.model(**body)
return self.model(**body), resp
@utils.add_req_id_to_generator()
def list(self, namespace, **kwargs):
"""Retrieve a listing of metadata objects.
@@ -379,18 +426,22 @@ class ObjectController(object):
resp, body = self.http_client.get(url)
for obj in body['objects']:
yield self.model(obj)
yield self.model(obj), resp
@utils.add_req_id_to_object()
def delete(self, namespace, object_name):
"""Delete an object."""
url = '/v2/metadefs/namespaces/{0}/objects/{1}'.format(namespace,
object_name)
self.http_client.delete(url)
resp, body = self.http_client.delete(url)
return (resp, body), resp
@utils.add_req_id_to_object()
def delete_all(self, namespace):
"""Delete all objects in a namespace."""
url = '/v2/metadefs/namespaces/{0}/objects'.format(namespace)
self.http_client.delete(url)
resp, body = self.http_client.delete(url)
return (resp, body), resp
class TagController(object):
@@ -404,6 +455,7 @@ class TagController(object):
return warlock.model_factory(schema.raw(),
base_class=schemas.SchemaBasedModel)
@utils.add_req_id_to_object()
def create(self, namespace, tag_name):
"""Create a tag.
@@ -416,8 +468,9 @@ class TagController(object):
resp, body = self.http_client.post(url)
body.pop('self', None)
return self.model(**body)
return self.model(**body), resp
@utils.add_req_id_to_generator()
def create_multiple(self, namespace, **kwargs):
"""Create the list of tags.
@@ -440,7 +493,7 @@ class TagController(object):
resp, body = self.http_client.post(url, data=tags)
body.pop('self', None)
for tag in body['tags']:
yield self.model(tag)
yield self.model(tag), resp
def update(self, namespace, tag_name, **kwargs):
"""Update a tag.
@@ -464,17 +517,28 @@ class TagController(object):
url = '/v2/metadefs/namespaces/{0}/tags/{1}'.format(namespace,
tag_name)
self.http_client.put(url, data=tag)
# Pass the original wrapped value to http client.
resp, _ = self.http_client.put(url, data=tag.wrapped)
# Get request id from `put` request so it can be passed to the
# following `get` call
req_id_hdr = {
'x-openstack-request-id': utils._extract_request_id(resp)}
return self.get(namespace, tag.name)
return self._get(namespace, tag.name, req_id_hdr)
def get(self, namespace, tag_name):
return self._get(namespace, tag_name)
@utils.add_req_id_to_object()
def _get(self, namespace, tag_name, header=None):
url = '/v2/metadefs/namespaces/{0}/tags/{1}'.format(namespace,
tag_name)
resp, body = self.http_client.get(url)
header = header or {}
resp, body = self.http_client.get(url, headers=header)
body.pop('self', None)
return self.model(**body)
return self.model(**body), resp
@utils.add_req_id_to_generator()
def list(self, namespace, **kwargs):
"""Retrieve a listing of metadata tags.
@@ -484,15 +548,19 @@ class TagController(object):
resp, body = self.http_client.get(url)
for tag in body['tags']:
yield self.model(tag)
yield self.model(tag), resp
@utils.add_req_id_to_object()
def delete(self, namespace, tag_name):
"""Delete a tag."""
url = '/v2/metadefs/namespaces/{0}/tags/{1}'.format(namespace,
tag_name)
self.http_client.delete(url)
resp, body = self.http_client.delete(url)
return (resp, body), resp
@utils.add_req_id_to_object()
def delete_all(self, namespace):
"""Delete all tags in a namespace."""
url = '/v2/metadefs/namespaces/{0}/tags'.format(namespace)
self.http_client.delete(url)
resp, body = self.http_client.delete(url)
return (resp, body), resp