Add microversion override for get and list
A continuation of previous patches adding support for node get and list calls to be able to overriden with an os_ironic_api_version keyword argument. Also adds a release note covering the prior patches in this series. Change-Id: I870540a23555e6ae37659452f727872d9d7882a3 Related-Bug: #1739440 Story: #2001870 Task: #14325
This commit is contained in:
parent
5448011fdf
commit
144ce25e42
@ -67,11 +67,13 @@ class Manager(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _get(self, resource_id, fields=None):
|
def _get(self, resource_id, fields=None, os_ironic_api_version=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
|
||||||
|
the request. If not specified, the client's default is used.
|
||||||
:raises exc.ValidationError: For invalid resource_id arg value.
|
:raises exc.ValidationError: For invalid resource_id arg value.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -85,19 +87,25 @@ class Manager(object):
|
|||||||
resource_id += ','.join(fields)
|
resource_id += ','.join(fields)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self._list(self._path(resource_id))[0]
|
return self._list(
|
||||||
|
self._path(resource_id),
|
||||||
|
os_ironic_api_version=os_ironic_api_version)[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):
|
||||||
"""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
|
||||||
|
the request. If not specified, the client's default is used.
|
||||||
: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)
|
||||||
if resource:
|
if resource:
|
||||||
return resource.to_dict()
|
return resource.to_dict()
|
||||||
else:
|
else:
|
||||||
@ -118,7 +126,7 @@ class Manager(object):
|
|||||||
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):
|
limit=None, os_ironic_api_version=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
|
||||||
@ -134,19 +142,24 @@ class Manager(object):
|
|||||||
:param obj_class: class for constructing the returned objects.
|
:param obj_class: class for constructing the returned objects.
|
||||||
:param limit: maximum number of items to return. If None returns
|
:param limit: maximum number of items to return. If None returns
|
||||||
everything.
|
everything.
|
||||||
|
: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.
|
||||||
"""
|
"""
|
||||||
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 = {}
|
||||||
|
if os_ironic_api_version is not None:
|
||||||
|
kwargs['headers'] = {'X-OpenStack-Ironic-API-Version':
|
||||||
|
os_ironic_api_version}
|
||||||
|
|
||||||
object_list = []
|
object_list = []
|
||||||
object_count = 0
|
object_count = 0
|
||||||
limit_reached = False
|
limit_reached = False
|
||||||
while url:
|
while url:
|
||||||
resp, body = self.api.json_request('GET', url)
|
resp, body = self.api.json_request('GET', url, **kwargs)
|
||||||
data = self._format_body_data(body, response_key)
|
data = self._format_body_data(body, response_key)
|
||||||
for obj in data:
|
for obj in data:
|
||||||
object_list.append(obj_class(self, obj, loaded=True))
|
object_list.append(obj_class(self, obj, loaded=True))
|
||||||
@ -170,16 +183,26 @@ class Manager(object):
|
|||||||
|
|
||||||
return object_list
|
return object_list
|
||||||
|
|
||||||
def __list(self, url, response_key=None, body=None):
|
def __list(self, url, response_key=None, body=None,
|
||||||
resp, body = self.api.json_request('GET', url)
|
os_ironic_api_version=None):
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
if os_ironic_api_version is not None:
|
||||||
|
kwargs['headers'] = {'X-OpenStack-Ironic-API-Version':
|
||||||
|
os_ironic_api_version}
|
||||||
|
|
||||||
|
resp, body = self.api.json_request('GET', url, **kwargs)
|
||||||
|
|
||||||
data = self._format_body_data(body, response_key)
|
data = self._format_body_data(body, response_key)
|
||||||
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):
|
||||||
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)
|
||||||
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):
|
||||||
|
@ -87,16 +87,16 @@ class TestableManager(base.CreateManager):
|
|||||||
return ('/v1/testableresources/%s' % id if id
|
return ('/v1/testableresources/%s' % id if id
|
||||||
else '/v1/testableresources')
|
else '/v1/testableresources')
|
||||||
|
|
||||||
def get(self, testable_resource_id, fields=None):
|
def get(self, testable_resource_id, fields=None, **kwargs):
|
||||||
return self._get(resource_id=testable_resource_id,
|
return self._get(resource_id=testable_resource_id,
|
||||||
fields=fields)
|
fields=fields, **kwargs)
|
||||||
|
|
||||||
def delete(self, testable_resource_id):
|
def delete(self, testable_resource_id, **kwargs):
|
||||||
return self._delete(resource_id=testable_resource_id)
|
return self._delete(resource_id=testable_resource_id, **kwargs)
|
||||||
|
|
||||||
def update(self, testable_resource_id, patch):
|
def update(self, testable_resource_id, patch, **kwargs):
|
||||||
return self._update(resource_id=testable_resource_id,
|
return self._update(resource_id=testable_resource_id,
|
||||||
patch=patch)
|
patch=patch, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ManagerTestCase(testtools.TestCase):
|
class ManagerTestCase(testtools.TestCase):
|
||||||
@ -120,12 +120,13 @@ class ManagerTestCase(testtools.TestCase):
|
|||||||
self.manager.create,
|
self.manager.create,
|
||||||
**INVALID_ATTRIBUTE_TESTABLE_RESOURCE)
|
**INVALID_ATTRIBUTE_TESTABLE_RESOURCE)
|
||||||
|
|
||||||
def test__get(self):
|
def test__get_microversion_override(self):
|
||||||
resource_id = TESTABLE_RESOURCE['uuid']
|
resource_id = TESTABLE_RESOURCE['uuid']
|
||||||
resource = self.manager._get(resource_id)
|
resource = self.manager._get(resource_id,
|
||||||
|
os_ironic_api_version='1.22')
|
||||||
expect = [
|
expect = [
|
||||||
('GET', '/v1/testableresources/%s' % resource_id,
|
('GET', '/v1/testableresources/%s' % resource_id,
|
||||||
{}, None),
|
{'X-OpenStack-Ironic-API-Version': '1.22'}, 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)
|
||||||
@ -147,12 +148,24 @@ 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):
|
||||||
|
resource_id = TESTABLE_RESOURCE['uuid']
|
||||||
|
resource = self.manager._get_as_dict(resource_id,
|
||||||
|
os_ironic_api_version='1.21')
|
||||||
|
expect = [
|
||||||
|
('GET', '/v1/testableresources/%s' % resource_id,
|
||||||
|
{'X-OpenStack-Ironic-API-Version': '1.21'}, None),
|
||||||
|
]
|
||||||
|
self.assertEqual(expect, self.api.calls)
|
||||||
|
self.assertEqual(TESTABLE_RESOURCE, resource)
|
||||||
|
|
||||||
@mock.patch.object(base.Manager, '_get', autospec=True)
|
@mock.patch.object(base.Manager, '_get', autospec=True)
|
||||||
def test__get_as_dict_empty(self, mock_get):
|
def test__get_as_dict_empty(self, mock_get):
|
||||||
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(mock.ANY, resource_id, fields=None,
|
||||||
|
os_ironic_api_version=None)
|
||||||
self.assertEqual({}, resource)
|
self.assertEqual({}, resource)
|
||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
@ -165,6 +178,17 @@ 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):
|
||||||
|
resource = self.manager.get(TESTABLE_RESOURCE['uuid'],
|
||||||
|
os_ironic_api_version='1.10')
|
||||||
|
expect = [
|
||||||
|
('GET', '/v1/testableresources/%s' % TESTABLE_RESOURCE['uuid'],
|
||||||
|
{'X-OpenStack-Ironic-API-Version': '1.10'}, None),
|
||||||
|
]
|
||||||
|
self.assertEqual(expect, self.api.calls)
|
||||||
|
self.assertEqual(TESTABLE_RESOURCE['uuid'], resource.uuid)
|
||||||
|
self.assertEqual(TESTABLE_RESOURCE['attribute1'], resource.attribute1)
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
patch = {'op': 'replace',
|
patch = {'op': 'replace',
|
||||||
'value': NEW_ATTRIBUTE_VALUE,
|
'value': NEW_ATTRIBUTE_VALUE,
|
||||||
@ -180,6 +204,21 @@ 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):
|
||||||
|
patch = {'op': 'replace',
|
||||||
|
'value': NEW_ATTRIBUTE_VALUE,
|
||||||
|
'path': '/attribute1'}
|
||||||
|
resource = self.manager.update(
|
||||||
|
testable_resource_id=TESTABLE_RESOURCE['uuid'],
|
||||||
|
patch=patch, os_ironic_api_version='1.9'
|
||||||
|
)
|
||||||
|
expect = [
|
||||||
|
('PATCH', '/v1/testableresources/%s' % TESTABLE_RESOURCE['uuid'],
|
||||||
|
{'X-OpenStack-Ironic-API-Version': '1.9'}, patch),
|
||||||
|
]
|
||||||
|
self.assertEqual(expect, self.api.calls)
|
||||||
|
self.assertEqual(NEW_ATTRIBUTE_VALUE, resource.attribute1)
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
resource = self.manager.delete(
|
resource = self.manager.delete(
|
||||||
testable_resource_id=TESTABLE_RESOURCE['uuid']
|
testable_resource_id=TESTABLE_RESOURCE['uuid']
|
||||||
|
@ -817,6 +817,16 @@ class NodeManagerTest(testtools.TestCase):
|
|||||||
self.assertEqual(2, len(nodes))
|
self.assertEqual(2, len(nodes))
|
||||||
self.assertEqual(nodes[0].extra, {})
|
self.assertEqual(nodes[0].extra, {})
|
||||||
|
|
||||||
|
def test_node_list_detail_microversion_override(self):
|
||||||
|
nodes = self.mgr.list(detail=True, os_ironic_api_version='1.30')
|
||||||
|
expect = [
|
||||||
|
('GET', '/v1/nodes/detail',
|
||||||
|
{'X-OpenStack-Ironic-API-Version': '1.30'}, None),
|
||||||
|
]
|
||||||
|
self.assertEqual(expect, self.api.calls)
|
||||||
|
self.assertEqual(2, len(nodes))
|
||||||
|
self.assertEqual(nodes[0].extra, {})
|
||||||
|
|
||||||
def test_node_list_fields(self):
|
def test_node_list_fields(self):
|
||||||
nodes = self.mgr.list(fields=['uuid', 'extra'])
|
nodes = self.mgr.list(fields=['uuid', 'extra'])
|
||||||
expect = [
|
expect = [
|
||||||
@ -898,6 +908,19 @@ class NodeManagerTest(testtools.TestCase):
|
|||||||
self.assertEqual(expect, self.api.calls)
|
self.assertEqual(expect, self.api.calls)
|
||||||
self.assertEqual(NEW_DRIVER, node.driver)
|
self.assertEqual(NEW_DRIVER, node.driver)
|
||||||
|
|
||||||
|
def test_update_microversion_override(self):
|
||||||
|
patch = {'op': 'replace',
|
||||||
|
'value': NEW_DRIVER,
|
||||||
|
'path': '/driver'}
|
||||||
|
node = self.mgr.update(node_id=NODE1['uuid'], patch=patch,
|
||||||
|
os_ironic_api_version='1.24')
|
||||||
|
expect = [
|
||||||
|
('PATCH', '/v1/nodes/%s' % NODE1['uuid'],
|
||||||
|
{'X-OpenStack-Ironic-API-Version': '1.24'}, patch),
|
||||||
|
]
|
||||||
|
self.assertEqual(expect, self.api.calls)
|
||||||
|
self.assertEqual(NEW_DRIVER, node.driver)
|
||||||
|
|
||||||
def test_node_port_list_with_uuid(self):
|
def test_node_port_list_with_uuid(self):
|
||||||
ports = self.mgr.list_ports(NODE1['uuid'])
|
ports = self.mgr.list_ports(NODE1['uuid'])
|
||||||
expect = [
|
expect = [
|
||||||
|
@ -58,7 +58,7 @@ class NodeManager(base.CreateManager):
|
|||||||
def list(self, associated=None, maintenance=None, marker=None, limit=None,
|
def list(self, associated=None, maintenance=None, marker=None, limit=None,
|
||||||
detail=False, sort_key=None, sort_dir=None, fields=None,
|
detail=False, sort_key=None, sort_dir=None, fields=None,
|
||||||
provision_state=None, driver=None, resource_class=None,
|
provision_state=None, driver=None, resource_class=None,
|
||||||
chassis=None, fault=None):
|
chassis=None, fault=None, os_ironic_api_version=None):
|
||||||
"""Retrieve a list of nodes.
|
"""Retrieve a list of nodes.
|
||||||
|
|
||||||
:param associated: Optional. Either a Boolean or a string
|
:param associated: Optional. Either a Boolean or a string
|
||||||
@ -107,6 +107,8 @@ class NodeManager(base.CreateManager):
|
|||||||
|
|
||||||
:param fault: Optional. String value to get only nodes with
|
:param fault: Optional. String value to get only nodes with
|
||||||
specified fault.
|
specified fault.
|
||||||
|
: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.
|
||||||
|
|
||||||
:returns: A list of nodes.
|
:returns: A list of nodes.
|
||||||
|
|
||||||
@ -142,10 +144,12 @@ class NodeManager(base.CreateManager):
|
|||||||
path += '?' + '&'.join(filters)
|
path += '?' + '&'.join(filters)
|
||||||
|
|
||||||
if limit is None:
|
if limit is None:
|
||||||
return self._list(self._path(path), "nodes")
|
return self._list(self._path(path), "nodes",
|
||||||
|
os_ironic_api_version=os_ironic_api_version)
|
||||||
else:
|
else:
|
||||||
return self._list_pagination(self._path(path), "nodes",
|
return self._list_pagination(
|
||||||
limit=limit)
|
self._path(path), "nodes", limit=limit,
|
||||||
|
os_ironic_api_version=os_ironic_api_version)
|
||||||
|
|
||||||
def list_ports(self, node_id, marker=None, limit=None, sort_key=None,
|
def list_ports(self, node_id, marker=None, limit=None, sort_key=None,
|
||||||
sort_dir=None, detail=False, fields=None):
|
sort_dir=None, detail=False, fields=None):
|
||||||
@ -314,8 +318,9 @@ class NodeManager(base.CreateManager):
|
|||||||
self._path(path), response_key="targets", limit=limit,
|
self._path(path), response_key="targets", limit=limit,
|
||||||
obj_class=volume_target.VolumeTarget)
|
obj_class=volume_target.VolumeTarget)
|
||||||
|
|
||||||
def get(self, node_id, fields=None):
|
def get(self, node_id, fields=None, os_ironic_api_version=None):
|
||||||
return self._get(resource_id=node_id, fields=fields)
|
return self._get(resource_id=node_id, fields=fields,
|
||||||
|
os_ironic_api_version=os_ironic_api_version)
|
||||||
|
|
||||||
def get_by_instance_uuid(self, instance_uuid, fields=None):
|
def get_by_instance_uuid(self, instance_uuid, fields=None):
|
||||||
path = '?instance_uuid=%s' % instance_uuid
|
path = '?instance_uuid=%s' % instance_uuid
|
||||||
|
11
releasenotes/notes/version-overrides-4e9ba1266a238c6a.yaml
Normal file
11
releasenotes/notes/version-overrides-4e9ba1266a238c6a.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Adds support for ``NodeManager.set_provision_state``,
|
||||||
|
``NodeManager.update``, ``NodeManager.get``, and ``NodeManager.list``
|
||||||
|
to accept an ``os_ironic_api_version`` keyword argument to override
|
||||||
|
the API version for that specific call to the REST API.
|
||||||
|
|
||||||
|
When overridden, the API version is not preserved, and if an unsupported
|
||||||
|
version is requested from the remote API, an ``UnsupportedVersion``
|
||||||
|
exception is raised.
|
Loading…
x
Reference in New Issue
Block a user