Allow to load a subset of object fields from DB
Currently, the logic contained inside the _from_db_object base ironic object method is simply assignment of values in the DB object to the oslo versioned object. This might not be that simple in case of nested objects, for example in case of tags contained inside the node. This change adds a possibility to select which fields to set as usual by passing them as fields argument to _from_db_object method. Other fields are loaded in the new _set_from_db_object method. Change-Id: Ib2ca1acc52b9ba05297976f2c7ad67a63e07dbde
This commit is contained in:
@@ -143,8 +143,19 @@ class IronicObject(object_base.VersionedObject):
|
||||
|
||||
return self.__class__.VERSION
|
||||
|
||||
def _set_from_db_object(self, context, db_object, fields=None):
|
||||
"""Sets object fields.
|
||||
|
||||
:param context: security context
|
||||
:param db_object: A DB entity of the object
|
||||
:param fields: list of fields to set on obj from values from db_object.
|
||||
"""
|
||||
fields = fields or self.fields
|
||||
for field in fields:
|
||||
self[field] = db_object[field]
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(context, obj, db_object):
|
||||
def _from_db_object(context, obj, db_object, fields=None):
|
||||
"""Converts a database entity to a formal object.
|
||||
|
||||
This always converts the database entity to the latest version
|
||||
@@ -156,6 +167,7 @@ class IronicObject(object_base.VersionedObject):
|
||||
:param context: security context
|
||||
:param obj: An object of the class.
|
||||
:param db_object: A DB entity of the object
|
||||
:param fields: list of fields to set on obj from values from db_object.
|
||||
:return: The object of the class with the database entity added
|
||||
:raises: ovo_exception.IncompatibleObjectVersion
|
||||
"""
|
||||
@@ -183,8 +195,7 @@ class IronicObject(object_base.VersionedObject):
|
||||
objname=objname, objver=db_version,
|
||||
supported=obj.__class__.VERSION)
|
||||
|
||||
for field in obj.fields:
|
||||
obj[field] = db_object[field]
|
||||
obj._set_from_db_object(context, db_object, fields)
|
||||
|
||||
obj._context = context
|
||||
|
||||
|
||||
@@ -38,8 +38,16 @@ class MyObj(base.IronicObject, object_base.VersionedObjectDictCompat):
|
||||
fields = {'foo': fields.IntegerField(),
|
||||
'bar': fields.StringField(),
|
||||
'missing': fields.StringField(),
|
||||
# nested object added as string for simplicity
|
||||
'nested_object': fields.StringField(),
|
||||
}
|
||||
|
||||
def _set_from_db_object(self, context, db_object, fields=None):
|
||||
fields = set(fields or self.fields) - {'nested_object'}
|
||||
super(MyObj, self)._set_from_db_object(context, db_object, fields)
|
||||
# some special manipulation with nested_object here
|
||||
self['nested_object'] = db_object.get('nested_object', '') + 'test'
|
||||
|
||||
def obj_load_attr(self, attrname):
|
||||
if attrname == 'version':
|
||||
setattr(self, attrname, None)
|
||||
@@ -332,7 +340,7 @@ class _TestObject(object):
|
||||
|
||||
def test_object_inheritance(self):
|
||||
base_fields = list(base.IronicObject.fields)
|
||||
myobj_fields = ['foo', 'bar', 'missing'] + base_fields
|
||||
myobj_fields = ['foo', 'bar', 'missing', 'nested_object'] + base_fields
|
||||
myobj3_fields = ['new_field']
|
||||
self.assertTrue(issubclass(TestSubclassedObject, MyObj))
|
||||
self.assertEqual(len(myobj_fields), len(MyObj.fields))
|
||||
@@ -503,6 +511,34 @@ class _TestObject(object):
|
||||
self.assertEqual('test', obj.bar)
|
||||
self.assertEqual('foo', obj.missing)
|
||||
|
||||
@mock.patch('ironic.common.release_mappings.RELEASE_MAPPING',
|
||||
autospec=True)
|
||||
def test__from_db_object_no_version_subset_of_fields(self,
|
||||
mock_release_mapping):
|
||||
# DB doesn't have version; get it from mapping
|
||||
mock_release_mapping.__getitem__.return_value = {
|
||||
'objects': {
|
||||
'MyObj': '1.5',
|
||||
}
|
||||
}
|
||||
obj = MyObj(self.context)
|
||||
dbobj = {'created_at': timeutils.utcnow(),
|
||||
'updated_at': timeutils.utcnow(),
|
||||
'version': None,
|
||||
'foo': 123, 'bar': 'test', 'missing': '',
|
||||
'nested_object': 'test'}
|
||||
# Mock obj_load_attr as this is what is called if an attribute that we
|
||||
# try to access is not yet set. For all ironic objects it's a noop,
|
||||
# we've implemented it in MyObj purely for testing
|
||||
with mock.patch.object(obj, 'obj_load_attr'):
|
||||
MyObj._from_db_object(self.context, obj, dbobj,
|
||||
fields=['foo', 'bar'])
|
||||
self.assertEqual(obj.__class__.VERSION, obj.VERSION)
|
||||
self.assertEqual(123, obj.foo)
|
||||
self.assertEqual('test', obj.bar)
|
||||
self.assertRaises(AttributeError, getattr, obj, 'missing')
|
||||
self.assertEqual('testtest', obj.nested_object)
|
||||
|
||||
@mock.patch('ironic.common.release_mappings.RELEASE_MAPPING',
|
||||
autospec=True)
|
||||
def test__from_db_object_map_version_bad(self, mock_release_mapping):
|
||||
@@ -626,7 +662,7 @@ class TestObject(_LocalTest, _TestObject):
|
||||
# The fingerprint values should only be changed if there is a version bump.
|
||||
expected_object_fingerprints = {
|
||||
'Node': '1.21-52674c214141cf3e09f8688bfed54577',
|
||||
'MyObj': '1.5-4f5efe8f0fcaf182bbe1c7fe3ba858db',
|
||||
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
|
||||
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
|
||||
'Port': '1.7-898a47921f4a1f53fcdddd4eeb179e0b',
|
||||
'Portgroup': '1.3-71923a81a86743b313b190f5c675e258',
|
||||
|
||||
Reference in New Issue
Block a user