Add global_request_id into the base client methods

This change also allows passing os_ironic_api_version into create and
delete methods, which was not possible previously.

Story: 2007611
Task: 39602
Change-Id: I28e094df8ca0b1921fb8b6d503e8d09dfff064d7
This commit is contained in:
Vladyslav Drok
2020-05-05 19:44:30 +02:00
parent 9f2d080c89
commit 76a16eade6
2 changed files with 121 additions and 41 deletions

View File

@@ -66,13 +66,16 @@ class Manager(object, metaclass=abc.ABCMeta):
""" """
def _get(self, resource_id, fields=None, os_ironic_api_version=None): def _get(self, resource_id, fields=None, os_ironic_api_version=None,
global_request_id=None):
"""Retrieve a resource. """Retrieve a resource.
:param resource_id: Identifier of the resource. :param resource_id: Identifier of the resource.
:param fields: List of specific fields to be returned. :param fields: List of specific fields to be returned.
:param os_ironic_api_version: String version (e.g. "1.35") to use for :param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used. the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
:raises exc.ValidationError: For invalid resource_id arg value. :raises exc.ValidationError: For invalid resource_id arg value.
""" """
@@ -88,23 +91,27 @@ class Manager(object, metaclass=abc.ABCMeta):
try: try:
return self._list( return self._list(
self._path(resource_id), self._path(resource_id),
os_ironic_api_version=os_ironic_api_version)[0] os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)[0]
except IndexError: except IndexError:
return None return None
def _get_as_dict(self, resource_id, fields=None, def _get_as_dict(self, resource_id, fields=None,
os_ironic_api_version=None): os_ironic_api_version=None, global_request_id=None):
"""Retrieve a resource as a dictionary """Retrieve a resource as a dictionary
:param resource_id: Identifier of the resource. :param resource_id: Identifier of the resource.
:param fields: List of specific fields to be returned. :param fields: List of specific fields to be returned.
:param os_ironic_api_version: String version (e.g. "1.35") to use for :param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used. the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
:returns: a dictionary representing the resource; may be empty :returns: a dictionary representing the resource; may be empty
""" """
resource = self._get(resource_id, fields=fields, resource = self._get(resource_id, fields=fields,
os_ironic_api_version=os_ironic_api_version) os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)
if resource: if resource:
return resource.to_dict() return resource.to_dict()
else: else:
@@ -125,7 +132,8 @@ class Manager(object, metaclass=abc.ABCMeta):
return data return data
def _list_pagination(self, url, response_key=None, obj_class=None, def _list_pagination(self, url, response_key=None, obj_class=None,
limit=None, os_ironic_api_version=None): limit=None, os_ironic_api_version=None,
global_request_id=None):
"""Retrieve a list of items. """Retrieve a list of items.
The Ironic API is configured to return a maximum number of The Ironic API is configured to return a maximum number of
@@ -143,16 +151,20 @@ class Manager(object, metaclass=abc.ABCMeta):
everything. everything.
:param os_ironic_api_version: String version (e.g. "1.35") to use for :param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used. the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
""" """
if obj_class is None: if obj_class is None:
obj_class = self.resource_class obj_class = self.resource_class
if limit is not None: if limit is not None:
limit = int(limit) limit = int(limit)
kwargs = {} kwargs = {"headers": {}}
if os_ironic_api_version is not None: if os_ironic_api_version is not None:
kwargs['headers'] = {'X-OpenStack-Ironic-API-Version': kwargs['headers'][
os_ironic_api_version} 'X-OpenStack-Ironic-API-Version'] = os_ironic_api_version
if global_request_id is not None:
kwargs["headers"]["X-Openstack-Request-Id"] = global_request_id
# NOTE(jroll) # NOTE(jroll)
# endpoint_trimmed is what is prepended if we only pass a path # endpoint_trimmed is what is prepended if we only pass a path
@@ -200,12 +212,14 @@ class Manager(object, metaclass=abc.ABCMeta):
return object_list return object_list
def __list(self, url, response_key=None, body=None, def __list(self, url, response_key=None, body=None,
os_ironic_api_version=None): os_ironic_api_version=None, global_request_id=None):
kwargs = {} kwargs = {"headers": {}}
if os_ironic_api_version is not None: if os_ironic_api_version is not None:
kwargs['headers'] = {'X-OpenStack-Ironic-API-Version': kwargs['headers'][
os_ironic_api_version} 'X-OpenStack-Ironic-API-Version'] = os_ironic_api_version
if global_request_id is not None:
kwargs["headers"]["X-Openstack-Request-Id"] = global_request_id
resp, body = self.api.json_request('GET', url, **kwargs) resp, body = self.api.json_request('GET', url, **kwargs)
@@ -213,19 +227,24 @@ class Manager(object, metaclass=abc.ABCMeta):
return data return data
def _list(self, url, response_key=None, obj_class=None, body=None, def _list(self, url, response_key=None, obj_class=None, body=None,
os_ironic_api_version=None): os_ironic_api_version=None, global_request_id=None):
if obj_class is None: if obj_class is None:
obj_class = self.resource_class obj_class = self.resource_class
data = self.__list(url, response_key=response_key, body=body, data = self.__list(url, response_key=response_key, body=body,
os_ironic_api_version=os_ironic_api_version) os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)
return [obj_class(self, res, loaded=True) for res in data if res] return [obj_class(self, res, loaded=True) for res in data if res]
def _list_primitives(self, url, response_key=None): def _list_primitives(self, url, response_key=None,
return self.__list(url, response_key=response_key) os_ironic_api_version=None, global_request_id=None):
return self.__list(url, response_key=response_key,
os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)
def _update(self, resource_id, patch, method='PATCH', def _update(self, resource_id, patch, method='PATCH',
os_ironic_api_version=None, params=None): os_ironic_api_version=None, global_request_id=None,
params=None):
"""Update a resource. """Update a resource.
:param resource_id: Resource identifier. :param resource_id: Resource identifier.
@@ -233,16 +252,20 @@ class Manager(object, metaclass=abc.ABCMeta):
:param method: Name of the method for the request. :param method: Name of the method for the request.
:param os_ironic_api_version: String version (e.g. "1.35") to use for :param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used. the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
:param params: query parameters to pass. :param params: query parameters to pass.
""" """
url = self._path(resource_id) url = self._path(resource_id)
kwargs = {} kwargs = {"headers": {}}
if patch is not None: if patch is not None:
kwargs['body'] = patch kwargs['body'] = patch
if os_ironic_api_version is not None: if os_ironic_api_version is not None:
kwargs['headers'] = {'X-OpenStack-Ironic-API-Version': kwargs['headers'][
os_ironic_api_version} 'X-OpenStack-Ironic-API-Version'] = os_ironic_api_version
if global_request_id is not None:
kwargs["headers"]["X-Openstack-Request-Id"] = global_request_id
if params: if params:
kwargs['params'] = params kwargs['params'] = params
resp, body = self.api.json_request(method, url, **kwargs) resp, body = self.api.json_request(method, url, **kwargs)
@@ -250,12 +273,23 @@ class Manager(object, metaclass=abc.ABCMeta):
if body: if body:
return self.resource_class(self, body) return self.resource_class(self, body)
def _delete(self, resource_id): def _delete(self, resource_id,
os_ironic_api_version=None, global_request_id=None):
"""Delete a resource. """Delete a resource.
:param resource_id: Resource identifier. :param resource_id: Resource identifier.
:param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
""" """
self.api.raw_request('DELETE', self._path(resource_id)) headers = {}
if os_ironic_api_version is not None:
headers["X-OpenStack-Ironic-API-Version"] = os_ironic_api_version
if global_request_id is not None:
headers["X-Openstack-Request-Id"] = global_request_id
self.api.raw_request('DELETE', self._path(resource_id),
headers=headers)
class CreateManager(Manager, metaclass=abc.ABCMeta): class CreateManager(Manager, metaclass=abc.ABCMeta):
@@ -268,11 +302,16 @@ class CreateManager(Manager, metaclass=abc.ABCMeta):
""" """
def create(self, **kwargs): def create(self, os_ironic_api_version=None, global_request_id=None,
**kwargs):
"""Create a resource based on a kwargs dictionary of attributes. """Create a resource based on a kwargs dictionary of attributes.
:param kwargs: A dictionary containing the attributes of the resource :param kwargs: A dictionary containing the attributes of the resource
that will be created. that will be created.
:param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
:raises exc.InvalidAttribute: For invalid attributes that are not :raises exc.InvalidAttribute: For invalid attributes that are not
needed to create the resource. needed to create the resource.
""" """
@@ -290,8 +329,14 @@ class CreateManager(Manager, metaclass=abc.ABCMeta):
'needed to create %(resource)s.' % 'needed to create %(resource)s.' %
{'resource': self._resource_name, {'resource': self._resource_name,
'attrs': '","'.join(invalid)}) 'attrs': '","'.join(invalid)})
headers = {}
if os_ironic_api_version is not None:
headers['X-OpenStack-Ironic-API-Version'] = os_ironic_api_version
if global_request_id is not None:
headers["X-Openstack-Request-Id"] = global_request_id
url = self._path() url = self._path()
resp, body = self.api.json_request('POST', url, body=new) resp, body = self.api.json_request('POST', url, body=new,
headers=headers)
if body: if body:
return self.resource_class(self, body) return self.resource_class(self, body)

View File

@@ -23,6 +23,7 @@ from ironicclient import exc
from ironicclient.tests.unit import utils from ironicclient.tests.unit import utils
REQ_ID = "req-A387D380-D384-486B-BC54-520342C13D03"
TESTABLE_RESOURCE = { TESTABLE_RESOURCE = {
'uuid': '11111111-2222-3333-4444-555555555555', 'uuid': '11111111-2222-3333-4444-555555555555',
'attribute1': '1', 'attribute1': '1',
@@ -115,18 +116,33 @@ class ManagerTestCase(testtools.TestCase):
self.assertTrue(resource) self.assertTrue(resource)
self.assertIsInstance(resource, TestableResource) self.assertIsInstance(resource, TestableResource)
def test_create_microversion_and_global_request_id_override(self):
resource = self.manager.create(
**CREATE_TESTABLE_RESOURCE,
os_ironic_api_version="1.22", global_request_id=REQ_ID)
expect = [
('POST', '/v1/testableresources',
{'X-OpenStack-Ironic-API-Version': '1.22',
'X-Openstack-Request-Id': REQ_ID}, CREATE_TESTABLE_RESOURCE),
]
self.assertEqual(expect, self.api.calls)
self.assertTrue(resource)
self.assertIsInstance(resource, TestableResource)
def test_create_with_invalid_attribute(self): def test_create_with_invalid_attribute(self):
self.assertRaisesRegex(exc.InvalidAttribute, "non-existent-attribute", self.assertRaisesRegex(exc.InvalidAttribute, "non-existent-attribute",
self.manager.create, self.manager.create,
**INVALID_ATTRIBUTE_TESTABLE_RESOURCE) **INVALID_ATTRIBUTE_TESTABLE_RESOURCE)
def test__get_microversion_override(self): def test__get_microversion_and_global_request_id_override(self):
resource_id = TESTABLE_RESOURCE['uuid'] resource_id = TESTABLE_RESOURCE['uuid']
resource = self.manager._get(resource_id, resource = self.manager._get(
os_ironic_api_version='1.22') resource_id, os_ironic_api_version='1.22',
global_request_id=REQ_ID)
expect = [ expect = [
('GET', '/v1/testableresources/%s' % resource_id, ('GET', '/v1/testableresources/%s' % resource_id,
{'X-OpenStack-Ironic-API-Version': '1.22'}, None), {'X-OpenStack-Ironic-API-Version': '1.22',
'X-Openstack-Request-Id': REQ_ID}, None),
] ]
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(resource_id, resource.uuid) self.assertEqual(resource_id, resource.uuid)
@@ -148,13 +164,15 @@ class ManagerTestCase(testtools.TestCase):
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(TESTABLE_RESOURCE, resource) self.assertEqual(TESTABLE_RESOURCE, resource)
def test__get_as_dict_microversion_override(self): def test__get_as_dict_microversion_and_global_request_id_override(self):
resource_id = TESTABLE_RESOURCE['uuid'] resource_id = TESTABLE_RESOURCE['uuid']
resource = self.manager._get_as_dict(resource_id, resource = self.manager._get_as_dict(
os_ironic_api_version='1.21') resource_id, os_ironic_api_version='1.21',
global_request_id=REQ_ID)
expect = [ expect = [
('GET', '/v1/testableresources/%s' % resource_id, ('GET', '/v1/testableresources/%s' % resource_id,
{'X-OpenStack-Ironic-API-Version': '1.21'}, None), {'X-OpenStack-Ironic-API-Version': '1.21',
'X-Openstack-Request-Id': REQ_ID}, None),
] ]
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(TESTABLE_RESOURCE, resource) self.assertEqual(TESTABLE_RESOURCE, resource)
@@ -164,8 +182,9 @@ class ManagerTestCase(testtools.TestCase):
mock_get.return_value = None mock_get.return_value = None
resource_id = TESTABLE_RESOURCE['uuid'] resource_id = TESTABLE_RESOURCE['uuid']
resource = self.manager._get_as_dict(resource_id) resource = self.manager._get_as_dict(resource_id)
mock_get.assert_called_once_with(mock.ANY, resource_id, fields=None, mock_get.assert_called_once_with(
os_ironic_api_version=None) mock.ANY, resource_id, fields=None,
os_ironic_api_version=None, global_request_id=None)
self.assertEqual({}, resource) self.assertEqual({}, resource)
def test_get(self): def test_get(self):
@@ -178,12 +197,14 @@ class ManagerTestCase(testtools.TestCase):
self.assertEqual(TESTABLE_RESOURCE['uuid'], resource.uuid) self.assertEqual(TESTABLE_RESOURCE['uuid'], resource.uuid)
self.assertEqual(TESTABLE_RESOURCE['attribute1'], resource.attribute1) self.assertEqual(TESTABLE_RESOURCE['attribute1'], resource.attribute1)
def test_get_microversion_override(self): def test_get_microversion_and_global_request_id_override(self):
resource = self.manager.get(TESTABLE_RESOURCE['uuid'], resource = self.manager.get(
os_ironic_api_version='1.10') TESTABLE_RESOURCE['uuid'], os_ironic_api_version='1.10',
global_request_id=REQ_ID)
expect = [ expect = [
('GET', '/v1/testableresources/%s' % TESTABLE_RESOURCE['uuid'], ('GET', '/v1/testableresources/%s' % TESTABLE_RESOURCE['uuid'],
{'X-OpenStack-Ironic-API-Version': '1.10'}, None), {'X-OpenStack-Ironic-API-Version': '1.10',
'X-Openstack-Request-Id': REQ_ID}, None),
] ]
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(TESTABLE_RESOURCE['uuid'], resource.uuid) self.assertEqual(TESTABLE_RESOURCE['uuid'], resource.uuid)
@@ -204,17 +225,18 @@ class ManagerTestCase(testtools.TestCase):
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(NEW_ATTRIBUTE_VALUE, resource.attribute1) self.assertEqual(NEW_ATTRIBUTE_VALUE, resource.attribute1)
def test_update_microversion_override(self): def test_update_microversion_and_global_request_id_override(self):
patch = {'op': 'replace', patch = {'op': 'replace',
'value': NEW_ATTRIBUTE_VALUE, 'value': NEW_ATTRIBUTE_VALUE,
'path': '/attribute1'} 'path': '/attribute1'}
resource = self.manager.update( resource = self.manager.update(
testable_resource_id=TESTABLE_RESOURCE['uuid'], testable_resource_id=TESTABLE_RESOURCE['uuid'],
patch=patch, os_ironic_api_version='1.9' patch=patch, os_ironic_api_version='1.9', global_request_id=REQ_ID
) )
expect = [ expect = [
('PATCH', '/v1/testableresources/%s' % TESTABLE_RESOURCE['uuid'], ('PATCH', '/v1/testableresources/%s' % TESTABLE_RESOURCE['uuid'],
{'X-OpenStack-Ironic-API-Version': '1.9'}, patch), {'X-OpenStack-Ironic-API-Version': '1.9',
'X-Openstack-Request-Id': REQ_ID}, patch),
] ]
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(NEW_ATTRIBUTE_VALUE, resource.attribute1) self.assertEqual(NEW_ATTRIBUTE_VALUE, resource.attribute1)
@@ -229,3 +251,16 @@ class ManagerTestCase(testtools.TestCase):
] ]
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertIsNone(resource) self.assertIsNone(resource)
def test_delete_microversion_and_global_request_id_override(self):
resource = self.manager.delete(
testable_resource_id=TESTABLE_RESOURCE['uuid'],
os_ironic_api_version="1.9", global_request_id=REQ_ID
)
expect = [
('DELETE', '/v1/testableresources/%s' % TESTABLE_RESOURCE['uuid'],
{'X-OpenStack-Ironic-API-Version': '1.9',
'X-Openstack-Request-Id': REQ_ID}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertIsNone(resource)