re-orphan flavor after rpc deserialization

The flavor objects stored in the instance object should not be used
to lazy load any data from the db as they are not connected to real
flavors in the db. These flavor objects are orphaned when created
but durin rpc deserialization every deserialized nova object
automatically gets a valid context. This can lead to unintended
lazy loading of the projects field of such flavor objects during
notification payload generation.

This patch orphans the flavor objects stored in the instance object
after every deserialization.

Change-Id: I458f81931bad7874e951e1c0fd464d149f61b244
Related-Bug: #1653221
This commit is contained in:
Balazs Gibizer 2017-03-01 17:44:50 +01:00
parent cb01463af4
commit 0082c47896
2 changed files with 37 additions and 0 deletions

View File

@ -272,6 +272,19 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
self = super(Instance, cls)._obj_from_primitive(context, objver,
primitive)
self._reset_metadata_tracking()
# Note(gibi): The flavors stored in the instance should not be used
# for lazy load data from the db as these objects aren't connected to
# real flavor objects in the db. However during RPC deserialization
# every object gets a valid context so we have to orphan the flavors
# again.
if self.obj_attr_is_set('flavor'):
self.flavor._context = None
if self.obj_attr_is_set('old_flavor') and self.old_flavor:
self.old_flavor._context = None
if self.obj_attr_is_set('new_flavor') and self.new_flavor:
self.new_flavor._context = None
return self
@property

View File

@ -1518,6 +1518,30 @@ class _TestInstanceObject(object):
inst1 = inst1.obj_clone()
self.assertEqual(len(inst1.obj_what_changed()), 0)
def test_flavor_deserialization_orphans_flavor(self):
flavor = objects.Flavor(context=self.context,
name='test-flavor',
memory_mb=1024,
root_gb=0,
vcpus=1,
flavorid="m1.test")
flavor.create()
inst = objects.Instance(context=self.context,
user_id=self.context.user_id,
project_id=self.context.project_id,
flavor=flavor,
old_flavor=flavor,
new_flavor=flavor)
inst.create()
inst = objects.Instance.get_by_uuid(self.context,
uuid=inst.uuid,
expected_attrs=['flavor'])
self.assertIsNone(inst.flavor._context)
self.assertIsNone(inst.old_flavor._context)
self.assertIsNone(inst.new_flavor._context)
class TestInstanceObject(test_objects._LocalTest,
_TestInstanceObject):