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:
@@ -84,3 +84,10 @@ class ListMixin(object):
|
|||||||
base_path += '/detail'
|
base_path += '/detail'
|
||||||
return super(ListMixin, cls).list(session, paginated=True,
|
return super(ListMixin, cls).list(session, paginated=True,
|
||||||
base_path=base_path, **params)
|
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'
|
resources_key = 'chassis'
|
||||||
base_path = '/chassis'
|
base_path = '/chassis'
|
||||||
|
|
||||||
|
# Specifying fields became possible in 1.8.
|
||||||
|
_max_microversion = '1.8'
|
||||||
|
|
||||||
# capabilities
|
# capabilities
|
||||||
allow_create = True
|
allow_create = True
|
||||||
allow_fetch = True
|
allow_fetch = True
|
||||||
@@ -29,7 +32,7 @@ class Chassis(_common.ListMixin, resource.Resource):
|
|||||||
commit_jsonpatch = True
|
commit_jsonpatch = True
|
||||||
|
|
||||||
_query_mapping = resource.QueryParameters(
|
_query_mapping = resource.QueryParameters(
|
||||||
'fields'
|
fields={'name': 'fields', 'type': _common.comma_separated_list},
|
||||||
)
|
)
|
||||||
|
|
||||||
#: Timestamp at which the chassis was created.
|
#: Timestamp at which the chassis was created.
|
||||||
|
@@ -49,8 +49,9 @@ class Node(_common.ListMixin, resource.Resource):
|
|||||||
commit_jsonpatch = True
|
commit_jsonpatch = True
|
||||||
|
|
||||||
_query_mapping = resource.QueryParameters(
|
_query_mapping = resource.QueryParameters(
|
||||||
'associated', 'conductor_group', 'driver', 'fault', 'fields',
|
'associated', 'conductor_group', 'driver', 'fault',
|
||||||
'provision_state', 'resource_class',
|
'provision_state', 'resource_class',
|
||||||
|
fields={'name': 'fields', 'type': _common.comma_separated_list},
|
||||||
instance_id='instance_uuid',
|
instance_id='instance_uuid',
|
||||||
is_maintenance='maintenance',
|
is_maintenance='maintenance',
|
||||||
)
|
)
|
||||||
|
@@ -29,7 +29,8 @@ class Port(_common.ListMixin, resource.Resource):
|
|||||||
commit_jsonpatch = True
|
commit_jsonpatch = True
|
||||||
|
|
||||||
_query_mapping = resource.QueryParameters(
|
_query_mapping = resource.QueryParameters(
|
||||||
'address', 'fields', 'node', 'portgroup',
|
'address', 'node', 'portgroup',
|
||||||
|
fields={'name': 'fields', 'type': _common.comma_separated_list},
|
||||||
node_id='node_uuid',
|
node_id='node_uuid',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -29,7 +29,8 @@ class PortGroup(_common.ListMixin, resource.Resource):
|
|||||||
commit_jsonpatch = True
|
commit_jsonpatch = True
|
||||||
|
|
||||||
_query_mapping = resource.QueryParameters(
|
_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.
|
# 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
|
:param mappings: Key-value pairs where the key is the client-side
|
||||||
name we'll accept here and the value is the name
|
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
|
By default, both limit and marker are included in the initial mapping
|
||||||
as they're the most common query parameters used for listing resources.
|
as they're the most common query parameters used for listing resources.
|
||||||
"""
|
"""
|
||||||
self._mapping = {"limit": "limit", "marker": "marker"}
|
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):
|
def _validate(self, query, base_path=None):
|
||||||
"""Check that supplied query keys match known query mappings
|
"""Check that supplied query keys match known query mappings
|
||||||
@@ -290,7 +295,9 @@ class QueryParameters(object):
|
|||||||
the resource.
|
the resource.
|
||||||
"""
|
"""
|
||||||
expected_params = list(self._mapping.keys())
|
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:
|
if base_path:
|
||||||
expected_params += utils.get_string_format_keys(base_path)
|
expected_params += utils.get_string_format_keys(base_path)
|
||||||
@@ -312,11 +319,25 @@ class QueryParameters(object):
|
|||||||
server side name.
|
server side name.
|
||||||
"""
|
"""
|
||||||
result = {}
|
result = {}
|
||||||
for key, value in self._mapping.items():
|
for client_side, server_side in self._mapping.items():
|
||||||
if key in query:
|
if isinstance(server_side, dict):
|
||||||
result[value] = query[key]
|
name = server_side['name']
|
||||||
elif value in query:
|
type_ = server_side.get('type')
|
||||||
result[value] = query[value]
|
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
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@@ -49,3 +49,15 @@ class TestBareMetalChassis(base.BaseBaremetalTest):
|
|||||||
ignore_missing=False)
|
ignore_missing=False)
|
||||||
self.assertIsNone(self.conn.baremetal.find_chassis(uuid))
|
self.assertIsNone(self.conn.baremetal.find_chassis(uuid))
|
||||||
self.assertIsNone(self.conn.baremetal.delete_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))
|
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):
|
class TestBareMetalVif(base.BaseBaremetalTest):
|
||||||
|
|
||||||
min_microversion = '1.28'
|
min_microversion = '1.28'
|
||||||
|
@@ -92,3 +92,16 @@ class TestBareMetalPort(base.BaseBaremetalTest):
|
|||||||
pxe_enabled=True)
|
pxe_enabled=True)
|
||||||
self.assertIsNone(self.conn.baremetal.find_port(uuid))
|
self.assertIsNone(self.conn.baremetal.find_port(uuid))
|
||||||
self.assertIsNone(self.conn.baremetal.delete_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)
|
ignore_missing=False)
|
||||||
self.assertIsNone(self.conn.baremetal.find_port_group(uuid))
|
self.assertIsNone(self.conn.baremetal.find_port_group(uuid))
|
||||||
self.assertIsNone(self.conn.baremetal.delete_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):
|
def test_create(self):
|
||||||
location = "location"
|
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)
|
sot = resource.QueryParameters(location, **mapping)
|
||||||
|
|
||||||
self.assertEqual({"location": "location",
|
self.assertEqual({"location": "location",
|
||||||
"first_name": "first-name",
|
"first_name": "first-name",
|
||||||
|
"second_name": {"name": "second-name"},
|
||||||
|
"third_name": {"name": "third", "type": int},
|
||||||
"limit": "limit",
|
"limit": "limit",
|
||||||
"marker": "marker"},
|
"marker": "marker"},
|
||||||
sot._mapping)
|
sot._mapping)
|
||||||
|
|
||||||
def test_transpose_unmapped(self):
|
def test_transpose_unmapped(self):
|
||||||
location = "location"
|
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)
|
sot = resource.QueryParameters(location, **mapping)
|
||||||
result = sot._transpose({"location": "Brooklyn",
|
result = sot._transpose({"location": "Brooklyn",
|
||||||
"first_name": "Brian",
|
"first_name": "Brian",
|
||||||
|
"pet_name": "Meow",
|
||||||
|
"answer": "42",
|
||||||
"last_name": "Curtin"})
|
"last_name": "Curtin"})
|
||||||
|
|
||||||
# last_name isn't mapped and shouldn't be included
|
# 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)
|
result)
|
||||||
|
|
||||||
def test_transpose_not_in_query(self):
|
def test_transpose_not_in_query(self):
|
||||||
location = "location"
|
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)
|
sot = resource.QueryParameters(location, **mapping)
|
||||||
result = sot._transpose({"location": "Brooklyn"})
|
result = sot._transpose({"location": "Brooklyn"})
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Fixes specifying fields when listing bare metal resources.
|
Reference in New Issue
Block a user