Merge "Support nested objects and object lists in as_dict"

This commit is contained in:
Zuul 2018-02-22 04:13:22 +00:00 committed by Gerrit Code Review
commit a935a3ec56
7 changed files with 93 additions and 4 deletions

View File

@ -1051,7 +1051,7 @@ class Node(base.APIBase):
self.fields.append(k)
# TODO(jroll) is there a less hacky way to do this?
if k == 'traits' and kwargs.get('traits') is not None:
value = kwargs['traits'].get_trait_names()
value = [t['trait'] for t in kwargs['traits']['objects']]
else:
value = kwargs.get(k, wtypes.Unset)
setattr(self, k, value)

View File

@ -64,9 +64,21 @@ class IronicObject(object_base.VersionedObject):
}
def as_dict(self):
return dict((k, getattr(self, k))
"""Return the object represented as a dict.
The returned object is JSON-serialisable.
"""
def _attr_as_dict(field):
"""Return an attribute as a dict, handling nested objects."""
attr = getattr(self, field)
if isinstance(attr, IronicObject):
attr = attr.as_dict()
return attr
return dict((k, _attr_as_dict(k))
for k in self.fields
if hasattr(self, k))
if self.obj_attr_is_set(k))
def obj_refresh(self, loaded_object):
"""Applies updates for objects that inherit from base.IronicObject.
@ -320,6 +332,16 @@ class IronicObject(object_base.VersionedObject):
return changes
class IronicObjectListBase(object_base.ObjectListBase):
def as_dict(self):
"""Return the object represented as a dict.
The returned object is JSON-serialisable.
"""
return {'objects': [obj.as_dict() for obj in self.objects]}
class IronicObjectSerializer(object_base.VersionedObjectSerializer):
# Base class to use for object hydration
OBJ_BASE_CLASS = IronicObject

View File

@ -98,7 +98,7 @@ class Trait(base.IronicObject):
@base.IronicObjectRegistry.register
class TraitList(object_base.ObjectListBase, base.IronicObject):
class TraitList(base.IronicObjectListBase, base.IronicObject):
# Version 1.0: Initial version
VERSION = '1.0'

View File

@ -16,6 +16,7 @@
import datetime
import mock
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from testtools import matchers
@ -41,6 +42,8 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
d = self.node.as_dict()
self.assertEqual('fake', d['driver_info']['ipmi_password'])
self.assertEqual('data', d['instance_info']['configdrive'])
# Ensure the node can be serialised.
jsonutils.dumps(d)
def test_as_dict_secure(self):
self.node.driver_info['ipmi_password'] = 'fake'
@ -48,6 +51,17 @@ class TestNodeObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
d = self.node.as_dict(secure=True)
self.assertEqual('******', d['driver_info']['ipmi_password'])
self.assertEqual('******', d['instance_info']['configdrive'])
# Ensure the node can be serialised.
jsonutils.dumps(d)
def test_as_dict_with_traits(self):
self.fake_node['traits'] = ['CUSTOM_1']
self.node = obj_utils.get_test_node(self.ctxt, **self.fake_node)
d = self.node.as_dict()
expected_traits = {'objects': [{'trait': 'CUSTOM_1'}]}
self.assertEqual(expected_traits, d['traits'])
# Ensure the node can be serialised.
jsonutils.dumps(d)
def test_get_by_id(self):
node_id = self.fake_node['id']

View File

@ -620,6 +620,40 @@ class _TestObject(object):
self.assertIn("'TestObj' object does not support item assignment",
err_message)
def test_as_dict(self):
obj = MyObj(self.context)
obj.foo = 1
result = obj.as_dict()
expected = {'foo': 1}
self.assertEqual(expected, result)
def test_as_dict_with_nested_object(self):
@base.IronicObjectRegistry.register_if(False)
class TestObj(base.IronicObject,
object_base.VersionedObjectDictCompat):
fields = {'my_obj': fields.ObjectField('MyObj')}
obj1 = MyObj(self.context)
obj1.foo = 1
obj2 = TestObj(self.context)
obj2.my_obj = obj1
result = obj2.as_dict()
expected = {'my_obj': {'foo': 1}}
self.assertEqual(expected, result)
def test_as_dict_with_nested_object_list(self):
@base.IronicObjectRegistry.register_if(False)
class TestObj(base.IronicObjectListBase, base.IronicObject):
fields = {'objects': fields.ListOfObjectsField('MyObj')}
obj1 = MyObj(self.context)
obj1.foo = 1
obj2 = TestObj(self.context)
obj2.objects = [obj1]
result = obj2.as_dict()
expected = {'objects': [{'foo': 1}]}
self.assertEqual(expected, result)
class TestObject(_LocalTest, _TestObject):
pass

View File

@ -101,3 +101,15 @@ class TestTraitObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
result = trait_list.get_trait_names()
self.assertEqual([self.fake_trait['trait']], result)
def test_as_dict(self):
trait = objects.Trait(context=self.context,
node_id=self.fake_trait['node_id'],
trait=self.fake_trait['trait'])
trait_list = objects.TraitList(context=self.context, objects=[trait])
result = trait_list.as_dict()
expected = {'objects': [{'node_id': self.fake_trait['node_id'],
'trait': self.fake_trait['trait']}]}
self.assertEqual(expected, result)

View File

@ -0,0 +1,7 @@
---
fixes:
- |
Fixes an issue seen during cleaning when the node being cleaned has one or
more traits assigned. This issue caused cleaning to fail, and the node to
enter the ``clean failed`` state. See `bug 1750027
<https://bugs.launchpad.net/ironic/+bug/1750027>`_ for details.