Merge "Set stage for objects to handle selected field lists."

This commit is contained in:
Zuul 2021-07-02 17:15:25 +00:00 committed by Gerrit Code Review
commit c57a5e3639
3 changed files with 96 additions and 10 deletions

View File

@ -213,12 +213,16 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
raise exception.InvalidParameterValue(msg)
def _set_from_db_object(self, context, db_object, fields=None):
fields = set(fields or self.fields) - {'traits'}
super(Node, self)._set_from_db_object(context, db_object, fields)
self.traits = object_base.obj_make_list(
context, objects.TraitList(context),
objects.Trait, db_object['traits'])
self.traits.obj_reset_changes()
use_fields = set(fields or self.fields) - {'traits'}
super(Node, self)._set_from_db_object(context, db_object, use_fields)
if not fields or 'traits' in fields:
# NOTE(TheJulia): No reason to do additional work on a field
# selected query, unless they are seeking the traits themselves.
self.traits = object_base.obj_make_list(
context, objects.TraitList(context),
objects.Trait, db_object['traits'],
fields=['trait', 'version'])
self.traits.obj_reset_changes()
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
# methods can be used in the future to replace current explicit RPC calls.
@ -328,7 +332,6 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
automatically included. These are: id, version,
updated_at, created_at, owner, and lessee.
:returns: a list of :class:`Node` object.
"""
if fields:
# All requests must include version, updated_at, created_at

View File

@ -497,6 +497,38 @@ class TestListNodes(test_api_base.BaseApiTest):
# We always append "links"
self.assertCountEqual(['uuid', 'instance_info', 'links'], node)
def test_get_collection_fields_for_nova(self):
# Unit test which explicitly attempts to request traits along with
# all columns which nova would normally request as part of it's sync
# process.
fields = ('uuid,power_state,target_power_state,provision_state,'
'target_provision_state,last_error,maintenance,'
'instance_uuid,traits,resource_class')
trait_list = ['CUSTOM_TRAIT1', 'CUSTOM_RAID5']
for i in range(3):
node = obj_utils.create_test_node(
self.context, uuid=uuidutils.generate_uuid(),
instance_uuid=uuidutils.generate_uuid())
self.dbapi.set_node_traits(node.id, trait_list, '1.0')
data = self.get_json(
'/nodes?fields=%s' % fields,
headers={api_base.Version.string: str(api_v1.max_version())})
self.assertEqual(3, len(data['nodes']))
for node in data['nodes']:
# We always append "links"
self.assertCountEqual([
'uuid', 'power_state', 'target_power_state',
'provision_state', 'target_provision_state', 'last_error',
'maintenance', 'instance_uuid', 'traits', 'resource_class',
'links'], node)
# Ensure traits exists that have been created on the node.
# Sorted traits list is an artifact of the data being inserted
# as distinct rows and then collected back into a list.
# There is no contract around ordering for the results of traits.
self.assertEqual(sorted(trait_list, reverse=False), node['traits'])
def test_get_custom_fields_invalid_fields(self):
node = obj_utils.create_test_node(self.context,
chassis_id=self.chassis.id)

View File

@ -396,17 +396,68 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
self.assertThat(nodes, matchers.HasLength(1))
self.assertIsInstance(nodes[0], objects.Node)
self.assertEqual(self.context, nodes[0]._context)
self.assertIsInstance(nodes[0].traits, objects.TraitList)
def test_list_with_fields(self):
with mock.patch.object(self.dbapi, 'get_node_list',
autospec=True) as mock_get_list:
mock_get_list.return_value = [self.fake_node]
objects.Node.list(self.context, fields=['name'])
nodes = objects.Node.list(self.context,
fields=['name', 'uuid',
'provision_state'])
mock_get_list.assert_called_with(
filters=None, limit=None, marker=None, sort_key=None,
sort_dir=None,
fields=['id', 'name', 'version', 'updated_at', 'created_at',
'owner', 'lessee', 'driver', 'conductor_group'])
fields=['id', 'name', 'uuid', 'provision_state', 'version',
'updated_at', 'created_at', 'owner', 'lessee',
'driver', 'conductor_group'])
self.assertThat(nodes, matchers.HasLength(1))
self.assertEqual(self.fake_node['uuid'], nodes[0].uuid)
self.assertEqual(self.fake_node['provision_state'],
nodes[0].provision_state)
self.assertIsInstance(nodes[0].traits, objects.TraitList)
# Random assortment of fields which should not be present.
for field in ['power_state', 'instance_info', 'resource_class',
'automated_clean', 'properties', 'driver', 'traits']:
self.assertFalse(field not in dir(nodes[0]))
def test_list_with_fields_traits(self):
with mock.patch.object(self.dbapi, 'get_node_list',
autospec=True) as mock_get_list:
# Trait objects and ultimately rows have an underlying
# version. Since we've never changed it, it should just
# be one, but this is required for the oslo versioned
# objects code path to handle objects properly and
# navigate mid-upgrade cases.
self.fake_node['traits'] = [{
'trait': 'traitx', 'version': "1"}]
mock_get_list.return_value = [self.fake_node]
nodes = objects.Node.list(self.context,
fields=['uuid', 'provision_state',
'traits'])
self.assertThat(nodes, matchers.HasLength(1))
self.assertEqual(self.fake_node['uuid'], nodes[0].uuid)
self.assertEqual(self.fake_node['provision_state'],
nodes[0].provision_state)
self.assertIsInstance(nodes[0].traits, objects.TraitList)
self.assertEqual('traitx', nodes[0].traits[0].trait)
for field in ['power_state', 'instance_info', 'resource_class',
'automated_clean', 'properties', 'driver']:
self.assertFalse(field not in dir(nodes[0]))
def test_list_with_fields_empty_trait_present(self):
with mock.patch.object(self.dbapi, 'get_node_list',
autospec=True) as mock_get_list:
mock_get_list.return_value = [self.fake_node]
nodes = objects.Node.list(self.context,
fields=['uuid', 'provision_state',
'traits'])
self.assertThat(nodes, matchers.HasLength(1))
self.assertEqual(self.fake_node['uuid'], nodes[0].uuid)
self.assertEqual(self.fake_node['provision_state'],
nodes[0].provision_state)
self.assertIsInstance(nodes[0].traits, objects.TraitList)
self.assertIn('traits', nodes[0])
def test_reserve(self):
with mock.patch.object(self.dbapi, 'reserve_node',