diff --git a/nova/notifications/objects/flavor.py b/nova/notifications/objects/flavor.py index a659d1e64f1..03f95cb9eb3 100644 --- a/nova/notifications/objects/flavor.py +++ b/nova/notifications/objects/flavor.py @@ -75,6 +75,13 @@ class FlavorPayload(base.NotificationPayloadBase): def __init__(self, flavor, **kwargs): super(FlavorPayload, self).__init__(**kwargs) + if 'projects' not in flavor: + # NOTE(danms): If projects is not loaded in the flavor, + # don't attempt to load it. If we're in a child cell then + # we can't load the real flavor, and if we're a flavor on + # an instance then we don't want to anyway. + flavor = flavor.obj_clone() + flavor._context = None self.populate_schema(flavor=flavor) def obj_make_compatible(self, primitive, target_version): diff --git a/nova/objects/flavor.py b/nova/objects/flavor.py index 73768244b28..1d95948cf08 100644 --- a/nova/objects/flavor.py +++ b/nova/objects/flavor.py @@ -610,6 +610,11 @@ def destroy(self): db.flavor_destroy(self._context, self.flavorid) def _send_notification(self, action): + # NOTE(danms): Instead of making the below notification + # lazy-load projects (which is a problem for instance-bound + # flavors and compute-cell operations), just load them here. + if 'projects' not in self: + self._load_projects() notification_type = flavor_notification.FlavorNotification payload_type = flavor_notification.FlavorPayload diff --git a/nova/objects/instance.py b/nova/objects/instance.py index e54d2241cd6..ca805b7d6ec 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -272,19 +272,6 @@ def _obj_from_primitive(cls, context, objver, primitive): 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 diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index 394b8d9a0a0..13f59e9eac5 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -1518,30 +1518,6 @@ def test_obj_clone(self): 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):