Add Wrapper classes for list, dict, tuple
Added wrapper classes which are inherited from base data types list, tuple and dict. Each of these wrapper classes contains a 'request_ids' attribute which will be populated with a 'x-openstack-request_id' received in a header from a response body. This change is required to return 'request_id' from client to log request_id mappings of cross projects. Partial-Implements: blueprint return-request-id-to-caller Change-Id: I3aadb4d8bf675e20f2094b66a23ac20f455a99eb
This commit is contained in:
parent
faf8808162
commit
1619f11b9c
cinderclient
@ -26,6 +26,7 @@ Base utilities to build API operation managers and objects on top of.
|
||||
import abc
|
||||
import copy
|
||||
|
||||
from requests import Response
|
||||
import six
|
||||
from six.moves.urllib import parse
|
||||
|
||||
@ -409,7 +410,43 @@ class Extension(HookableMixin):
|
||||
return "<Extension '%s'>" % self.name
|
||||
|
||||
|
||||
class Resource(object):
|
||||
class RequestIdMixin(object):
|
||||
"""Wrapper class to expose x-openstack-request-id to the caller."""
|
||||
def setup(self):
|
||||
self.x_openstack_request_ids = []
|
||||
|
||||
@property
|
||||
def request_ids(self):
|
||||
return self.x_openstack_request_ids
|
||||
|
||||
def append_request_ids(self, resp):
|
||||
"""Add request_ids as an attribute to the object
|
||||
|
||||
:param resp: list, Response object or string
|
||||
"""
|
||||
if resp is None:
|
||||
return
|
||||
|
||||
if isinstance(resp, list):
|
||||
# Add list of request_ids if response is of type list.
|
||||
for resp_obj in resp:
|
||||
self._append_request_id(resp_obj)
|
||||
else:
|
||||
# Add request_ids if response contains single object.
|
||||
self._append_request_id(resp)
|
||||
|
||||
def _append_request_id(self, resp):
|
||||
if isinstance(resp, Response):
|
||||
# Extract 'x-openstack-request-id' from headers if
|
||||
# response is a Response object.
|
||||
request_id = resp.headers.get('x-openstack-request-id')
|
||||
self.x_openstack_request_ids.append(request_id)
|
||||
else:
|
||||
# If resp is of type string (in case of encryption type list)
|
||||
self.x_openstack_request_ids.append(resp)
|
||||
|
||||
|
||||
class Resource(RequestIdMixin):
|
||||
"""Base class for OpenStack resources (tenant, user, etc.).
|
||||
|
||||
This is pretty much just a bag for attributes.
|
||||
@ -418,22 +455,26 @@ class Resource(object):
|
||||
HUMAN_ID = False
|
||||
NAME_ATTR = 'name'
|
||||
|
||||
def __init__(self, manager, info, loaded=False):
|
||||
def __init__(self, manager, info, loaded=False, resp=None):
|
||||
"""Populate and bind to a manager.
|
||||
|
||||
:param manager: BaseManager object
|
||||
:param info: dictionary representing resource attributes
|
||||
:param loaded: prevent lazy-loading if set to True
|
||||
:param resp: Response or list of Response objects
|
||||
"""
|
||||
self.manager = manager
|
||||
self._info = info
|
||||
self._add_details(info)
|
||||
self._loaded = loaded
|
||||
self.setup()
|
||||
self.append_request_ids(resp)
|
||||
|
||||
def __repr__(self):
|
||||
reprkeys = sorted(k
|
||||
for k in self.__dict__.keys()
|
||||
if k[0] != '_' and k != 'manager')
|
||||
if k[0] != '_' and
|
||||
k not in ['manager', 'x_openstack_request_ids'])
|
||||
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
|
||||
return "<%s %s>" % (self.__class__.__name__, info)
|
||||
|
||||
@ -493,3 +534,26 @@ class Resource(object):
|
||||
|
||||
def to_dict(self):
|
||||
return copy.deepcopy(self._info)
|
||||
|
||||
|
||||
class ListWithMeta(list, RequestIdMixin):
|
||||
def __init__(self, values, resp):
|
||||
super(ListWithMeta, self).__init__(values)
|
||||
self.setup()
|
||||
self.append_request_ids(resp)
|
||||
|
||||
|
||||
class DictWithMeta(dict, RequestIdMixin):
|
||||
def __init__(self, values, resp):
|
||||
super(DictWithMeta, self).__init__(values)
|
||||
self.setup()
|
||||
self.append_request_ids(resp)
|
||||
|
||||
|
||||
class TupleWithMeta(tuple, RequestIdMixin):
|
||||
def __new__(cls, resp, values):
|
||||
return super(TupleWithMeta, cls).__new__(cls, (resp, values))
|
||||
|
||||
def __init__(self, resp, values):
|
||||
self.setup()
|
||||
self.append_request_ids(resp)
|
||||
|
@ -11,8 +11,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from requests import Response
|
||||
|
||||
from cinderclient import base
|
||||
from cinderclient import exceptions
|
||||
from cinderclient.openstack.common.apiclient import base as common_base
|
||||
from cinderclient.v1 import volumes
|
||||
from cinderclient.tests.unit import utils
|
||||
from cinderclient.tests.unit.v1 import fakes
|
||||
@ -21,11 +24,21 @@ from cinderclient.tests.unit.v1 import fakes
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
REQUEST_ID = 'req-test-request-id'
|
||||
|
||||
|
||||
def create_response_obj_with_header():
|
||||
resp = Response()
|
||||
resp.headers['x-openstack-request-id'] = REQUEST_ID
|
||||
return resp
|
||||
|
||||
|
||||
class BaseTest(utils.TestCase):
|
||||
|
||||
def test_resource_repr(self):
|
||||
r = base.Resource(None, dict(foo="bar", baz="spam"))
|
||||
self.assertEqual("<Resource baz=spam, foo=bar>", repr(r))
|
||||
self.assertNotIn("x_openstack_request_ids", repr(r))
|
||||
|
||||
def test_getid(self):
|
||||
self.assertEqual(4, base.getid(4))
|
||||
@ -63,3 +76,38 @@ class BaseTest(utils.TestCase):
|
||||
def test_to_dict(self):
|
||||
r1 = base.Resource(None, {'id': 1, 'name': 'hi'})
|
||||
self.assertEqual({'id': 1, 'name': 'hi'}, r1.to_dict())
|
||||
|
||||
def test_resource_object_with_request_ids(self):
|
||||
resp_obj = create_response_obj_with_header()
|
||||
r = base.Resource(None, {"name": "1"}, resp=resp_obj)
|
||||
self.assertEqual([REQUEST_ID], r.request_ids)
|
||||
|
||||
|
||||
class ListWithMetaTest(utils.TestCase):
|
||||
def test_list_with_meta(self):
|
||||
resp = create_response_obj_with_header()
|
||||
obj = common_base.ListWithMeta([], resp)
|
||||
self.assertEqual([], obj)
|
||||
# Check request_ids attribute is added to obj
|
||||
self.assertTrue(hasattr(obj, 'request_ids'))
|
||||
self.assertEqual([REQUEST_ID], obj.request_ids)
|
||||
|
||||
|
||||
class DictWithMetaTest(utils.TestCase):
|
||||
def test_dict_with_meta(self):
|
||||
resp = create_response_obj_with_header()
|
||||
obj = common_base.DictWithMeta([], resp)
|
||||
self.assertEqual({}, obj)
|
||||
# Check request_ids attribute is added to obj
|
||||
self.assertTrue(hasattr(obj, 'request_ids'))
|
||||
self.assertEqual([REQUEST_ID], obj.request_ids)
|
||||
|
||||
|
||||
class TupleWithMetaTest(utils.TestCase):
|
||||
def test_tuple_with_meta(self):
|
||||
resp = create_response_obj_with_header()
|
||||
obj = common_base.TupleWithMeta(resp, None)
|
||||
self.assertIsInstance(obj, tuple)
|
||||
# Check request_ids attribute is added to obj
|
||||
self.assertTrue(hasattr(obj, 'request_ids'))
|
||||
self.assertEqual([REQUEST_ID], obj.request_ids)
|
||||
|
Loading…
x
Reference in New Issue
Block a user