Fix requesting specific fields from ironic
Currently we try sending a Python dict in the query, which obviously does not work. Convert fields to a comma-separated list first. For chassis specify that the required API version 1.8 is supported. Change-Id: Ie1c3230f4fd14a59237a55bab2e91f04d50529a7
This commit is contained in:
parent
660d193623
commit
79dff76bef
|
@ -84,3 +84,10 @@ class ListMixin(object):
|
|||
base_path += '/detail'
|
||||
return super(ListMixin, cls).list(session, paginated=True,
|
||||
base_path=base_path, **params)
|
||||
|
||||
|
||||
def comma_separated_list(value):
|
||||
if value is None:
|
||||
return None
|
||||
else:
|
||||
return ','.join(value)
|
||||
|
|
|
@ -19,6 +19,9 @@ class Chassis(_common.ListMixin, resource.Resource):
|
|||
resources_key = 'chassis'
|
||||
base_path = '/chassis'
|
||||
|
||||
# Specifying fields became possible in 1.8.
|
||||
_max_microversion = '1.8'
|
||||
|
||||
# capabilities
|
||||
allow_create = True
|
||||
allow_fetch = True
|
||||
|
@ -29,7 +32,7 @@ class Chassis(_common.ListMixin, resource.Resource):
|
|||
commit_jsonpatch = True
|
||||
|
||||
_query_mapping = resource.QueryParameters(
|
||||
'fields'
|
||||
fields={'name': 'fields', 'type': _common.comma_separated_list},
|
||||
)
|
||||
|
||||
#: Timestamp at which the chassis was created.
|
||||
|
|
|
@ -49,8 +49,9 @@ class Node(_common.ListMixin, resource.Resource):
|
|||
commit_jsonpatch = True
|
||||
|
||||
_query_mapping = resource.QueryParameters(
|
||||
'associated', 'conductor_group', 'driver', 'fault', 'fields',
|
||||
'associated', 'conductor_group', 'driver', 'fault',
|
||||
'provision_state', 'resource_class',
|
||||
fields={'name': 'fields', 'type': _common.comma_separated_list},
|
||||
instance_id='instance_uuid',
|
||||
is_maintenance='maintenance',
|
||||
)
|
||||
|
|
|
@ -29,7 +29,8 @@ class Port(_common.ListMixin, resource.Resource):
|
|||
commit_jsonpatch = True
|
||||
|
||||
_query_mapping = resource.QueryParameters(
|
||||
'address', 'fields', 'node', 'portgroup',
|
||||
'address', 'node', 'portgroup',
|
||||
fields={'name': 'fields', 'type': _common.comma_separated_list},
|
||||
node_id='node_uuid',
|
||||
)
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@ class PortGroup(_common.ListMixin, resource.Resource):
|
|||
commit_jsonpatch = True
|
||||
|
||||
_query_mapping = resource.QueryParameters(
|
||||
'node', 'address', 'fields',
|
||||
'node', 'address',
|
||||
fields={'name': 'fields', 'type': _common.comma_separated_list},
|
||||
)
|
||||
|
||||
# The mode and properties field introduced in 1.26.
|
||||
|
|
|
@ -273,13 +273,18 @@ class QueryParameters(object):
|
|||
|
||||
:param mappings: Key-value pairs where the key is the client-side
|
||||
name we'll accept here and the value is the name
|
||||
the server expects, e.g, changes_since=changes-since
|
||||
the server expects, e.g, changes_since=changes-since.
|
||||
Additionally, a value can be a dict with optional keys
|
||||
name - server-side name,
|
||||
type - callable to convert from client to server
|
||||
representation.
|
||||
|
||||
By default, both limit and marker are included in the initial mapping
|
||||
as they're the most common query parameters used for listing resources.
|
||||
"""
|
||||
self._mapping = {"limit": "limit", "marker": "marker"}
|
||||
self._mapping.update(dict({name: name for name in names}, **mappings))
|
||||
self._mapping.update({name: name for name in names})
|
||||
self._mapping.update(mappings)
|
||||
|
||||
def _validate(self, query, base_path=None):
|
||||
"""Check that supplied query keys match known query mappings
|
||||
|
@ -290,7 +295,9 @@ class QueryParameters(object):
|
|||
the resource.
|
||||
"""
|
||||
expected_params = list(self._mapping.keys())
|
||||
expected_params += self._mapping.values()
|
||||
expected_params.extend(
|
||||
value['name'] if isinstance(value, dict) else value
|
||||
for value in self._mapping.values())
|
||||
|
||||
if base_path:
|
||||
expected_params += utils.get_string_format_keys(base_path)
|
||||
|
@ -312,11 +319,25 @@ class QueryParameters(object):
|
|||
server side name.
|
||||
"""
|
||||
result = {}
|
||||
for key, value in self._mapping.items():
|
||||
if key in query:
|
||||
result[value] = query[key]
|
||||
elif value in query:
|
||||
result[value] = query[value]
|
||||
for client_side, server_side in self._mapping.items():
|
||||
if isinstance(server_side, dict):
|
||||
name = server_side['name']
|
||||
type_ = server_side.get('type')
|
||||
else:
|
||||
name = server_side
|
||||
type_ = None
|
||||
|
||||
if client_side in query:
|
||||
value = query[client_side]
|
||||
elif name in query:
|
||||
value = query[name]
|
||||
else:
|
||||
continue
|
||||
|
||||
if type_ is not None:
|
||||
result[name] = type_(value)
|
||||
else:
|
||||
result[name] = value
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
@ -49,3 +49,15 @@ class TestBareMetalChassis(base.BaseBaremetalTest):
|
|||
ignore_missing=False)
|
||||
self.assertIsNone(self.conn.baremetal.find_chassis(uuid))
|
||||
self.assertIsNone(self.conn.baremetal.delete_chassis(uuid))
|
||||
|
||||
|
||||
class TestBareMetalChassisFields(base.BaseBaremetalTest):
|
||||
|
||||
min_microversion = '1.8'
|
||||
|
||||
def test_chassis_fields(self):
|
||||
self.create_chassis(description='something')
|
||||
result = self.conn.baremetal.chassis(fields=['uuid', 'extra'])
|
||||
for ch in result:
|
||||
self.assertIsNotNone(ch.id)
|
||||
self.assertIsNone(ch.description)
|
||||
|
|
|
@ -142,6 +142,18 @@ class TestBareMetalNode(base.BaseBaremetalTest):
|
|||
self.assertIsNone(self.conn.baremetal.delete_node(uuid))
|
||||
|
||||
|
||||
class TestBareMetalNodeFields(base.BaseBaremetalTest):
|
||||
|
||||
min_microversion = '1.8'
|
||||
|
||||
def test_node_fields(self):
|
||||
self.create_node()
|
||||
result = self.conn.baremetal.nodes(fields=['uuid', 'name'])
|
||||
for item in result:
|
||||
self.assertIsNotNone(item.id)
|
||||
self.assertIsNone(item.driver)
|
||||
|
||||
|
||||
class TestBareMetalVif(base.BaseBaremetalTest):
|
||||
|
||||
min_microversion = '1.28'
|
||||
|
|
|
@ -92,3 +92,16 @@ class TestBareMetalPort(base.BaseBaremetalTest):
|
|||
pxe_enabled=True)
|
||||
self.assertIsNone(self.conn.baremetal.find_port(uuid))
|
||||
self.assertIsNone(self.conn.baremetal.delete_port(uuid))
|
||||
|
||||
|
||||
class TestBareMetalPortFields(base.BaseBaremetalTest):
|
||||
|
||||
min_microversion = '1.8'
|
||||
|
||||
def test_port_fields(self):
|
||||
self.create_node()
|
||||
self.create_port(address='11:22:33:44:55:66')
|
||||
result = self.conn.baremetal.ports(fields=['uuid'])
|
||||
for item in result:
|
||||
self.assertIsNotNone(item.id)
|
||||
self.assertIsNone(item.address)
|
||||
|
|
|
@ -84,3 +84,11 @@ class TestBareMetalPortGroup(base.BaseBaremetalTest):
|
|||
ignore_missing=False)
|
||||
self.assertIsNone(self.conn.baremetal.find_port_group(uuid))
|
||||
self.assertIsNone(self.conn.baremetal.delete_port_group(uuid))
|
||||
|
||||
def test_port_group_fields(self):
|
||||
self.create_node()
|
||||
self.create_port_group(address='11:22:33:44:55:66')
|
||||
result = self.conn.baremetal.port_groups(fields=['uuid', 'name'])
|
||||
for item in result:
|
||||
self.assertIsNotNone(item.id)
|
||||
self.assertIsNone(item.address)
|
||||
|
|
|
@ -360,32 +360,43 @@ class TestQueryParameters(base.TestCase):
|
|||
|
||||
def test_create(self):
|
||||
location = "location"
|
||||
mapping = {"first_name": "first-name"}
|
||||
mapping = {"first_name": "first-name",
|
||||
"second_name": {"name": "second-name"},
|
||||
"third_name": {"name": "third", "type": int}}
|
||||
|
||||
sot = resource.QueryParameters(location, **mapping)
|
||||
|
||||
self.assertEqual({"location": "location",
|
||||
"first_name": "first-name",
|
||||
"second_name": {"name": "second-name"},
|
||||
"third_name": {"name": "third", "type": int},
|
||||
"limit": "limit",
|
||||
"marker": "marker"},
|
||||
sot._mapping)
|
||||
|
||||
def test_transpose_unmapped(self):
|
||||
location = "location"
|
||||
mapping = {"first_name": "first-name"}
|
||||
mapping = {"first_name": "first-name",
|
||||
"pet_name": {"name": "pet"},
|
||||
"answer": {"name": "answer", "type": int}}
|
||||
|
||||
sot = resource.QueryParameters(location, **mapping)
|
||||
result = sot._transpose({"location": "Brooklyn",
|
||||
"first_name": "Brian",
|
||||
"pet_name": "Meow",
|
||||
"answer": "42",
|
||||
"last_name": "Curtin"})
|
||||
|
||||
# last_name isn't mapped and shouldn't be included
|
||||
self.assertEqual({"location": "Brooklyn", "first-name": "Brian"},
|
||||
self.assertEqual({"location": "Brooklyn", "first-name": "Brian",
|
||||
"pet": "Meow", "answer": 42},
|
||||
result)
|
||||
|
||||
def test_transpose_not_in_query(self):
|
||||
location = "location"
|
||||
mapping = {"first_name": "first-name"}
|
||||
mapping = {"first_name": "first-name",
|
||||
"pet_name": {"name": "pet"},
|
||||
"answer": {"name": "answer", "type": int}}
|
||||
|
||||
sot = resource.QueryParameters(location, **mapping)
|
||||
result = sot._transpose({"location": "Brooklyn"})
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes specifying fields when listing bare metal resources.
|
Loading…
Reference in New Issue