diff options
-rw-r--r-- | nova/api/ec2/cloud.py | 3 | ||||
-rwxr-xr-x | nova/compute/manager.py | 3 | ||||
-rw-r--r-- | nova/objects/base.py | 26 | ||||
-rw-r--r-- | nova/objects/instance.py | 12 | ||||
-rw-r--r-- | nova/objects/instance_info_cache.py | 5 | ||||
-rw-r--r-- | nova/tests/objects/test_objects.py | 37 |
6 files changed, 74 insertions, 12 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index e02f7b6f9..51a86e02f 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -211,9 +211,8 @@ def db_to_inst_obj(context, db_instance): # NOTE(danms): This is a temporary helper method for converting # Instance DB objects to NovaObjects without needing to re-query. inst_obj = instance_obj.Instance._from_db_object( - instance_obj.Instance(), db_instance, + context, instance_obj.Instance(), db_instance, expected_attrs=['system_metadata', 'metadata']) - inst_obj._context = context return inst_obj diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 1a6479f7f..22881f5bd 100755 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -282,7 +282,8 @@ def object_compat(function): instance = kwargs['instance'] if isinstance(instance, dict): kwargs['instance'] = instance_obj.Instance._from_db_object( - instance_obj.Instance(), instance, expected_attrs=metas) + context, instance_obj.Instance(), instance, + expected_attrs=metas) kwargs['instance']._context = context return function(self, context, **kwargs) diff --git a/nova/objects/base.py b/nova/objects/base.py index f5fc37e03..6f2b6c81f 100644 --- a/nova/objects/base.py +++ b/nova/objects/base.py @@ -425,13 +425,35 @@ class NovaObjectSerializer(nova.openstack.common.rpc.serializer.Serializer): that needs to accept or return NovaObjects as arguments or result values should pass this to its RpcProxy and RpcDispatcher objects. """ + def _process_iterable(self, context, action_fn, values): + """Process an iterable, taking an action on each value. + :param:context: Request context + :param:action_fn: Action to take on each item in values + :param:values: Iterable container of things to take action on + :returns: A new container of the same type (except set) with + items from values having had action applied. + """ + iterable = values.__class__ + if iterable == set: + # NOTE(danms): A set can't have an unhashable value inside, such as + # a dict. Convert sets to tuples, which is fine, since we can't + # send them over RPC anyway. + iterable = tuple + return iterable([action_fn(context, value) for value in values]) + def serialize_entity(self, context, entity): - if (hasattr(entity, 'obj_to_primitive') and - callable(entity.obj_to_primitive)): + if isinstance(entity, (tuple, list, set)): + entity = self._process_iterable(context, self.serialize_entity, + entity) + elif (hasattr(entity, 'obj_to_primitive') and + callable(entity.obj_to_primitive)): entity = entity.obj_to_primitive() return entity def deserialize_entity(self, context, entity): if isinstance(entity, dict) and 'nova_object.name' in entity: entity = NovaObject.obj_from_primitive(entity, context=context) + elif isinstance(entity, (tuple, list, set)): + entity = self._process_iterable(context, self.deserialize_entity, + entity) return entity diff --git a/nova/objects/instance.py b/nova/objects/instance.py index de47f648f..55e98e46e 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -158,7 +158,7 @@ class Instance(base.NovaObject): return base.NovaObject.obj_from_primitive(val) @staticmethod - def _from_db_object(instance, db_inst, expected_attrs=None): + def _from_db_object(context, instance, db_inst, expected_attrs=None): """Method to help with migration to objects. Converts a database entity to a formal object. @@ -185,8 +185,9 @@ class Instance(base.NovaObject): if db_inst['info_cache']: instance['info_cache'] = instance_info_cache.InstanceInfoCache() instance_info_cache.InstanceInfoCache._from_db_object( - instance['info_cache'], db_inst['info_cache']) + context, instance['info_cache'], db_inst['info_cache']) + instance._context = context instance.obj_reset_changes() return instance @@ -207,7 +208,8 @@ class Instance(base.NovaObject): db_inst = db.instance_get_by_uuid(context, uuid, columns_to_join) - return Instance._from_db_object(cls(), db_inst, expected_attrs) + return Instance._from_db_object(context, cls(), db_inst, + expected_attrs) @base.remotable def save(self, context, expected_task_state=None): @@ -240,7 +242,7 @@ class Instance(base.NovaObject): for attr in INSTANCE_OPTIONAL_FIELDS: if hasattr(self, base.get_attrname(attr)): expected_attrs.append(attr) - Instance._from_db_object(self, inst_ref, expected_attrs) + Instance._from_db_object(context, self, inst_ref, expected_attrs) if 'vm_state' in changes or 'task_state' in changes: notifications.send_update(context, old_ref, inst_ref) @@ -282,7 +284,7 @@ class Instance(base.NovaObject): def _make_instance_list(context, inst_list, db_inst_list, expected_attrs): inst_list.objects = [] for db_inst in db_inst_list: - inst_obj = Instance._from_db_object(Instance(), db_inst, + inst_obj = Instance._from_db_object(context, Instance(), db_inst, expected_attrs=expected_attrs) inst_obj._context = context inst_list.objects.append(inst_obj) diff --git a/nova/objects/instance_info_cache.py b/nova/objects/instance_info_cache.py index 6b46559ed..a14175852 100644 --- a/nova/objects/instance_info_cache.py +++ b/nova/objects/instance_info_cache.py @@ -23,16 +23,17 @@ class InstanceInfoCache(base.NovaObject): } @staticmethod - def _from_db_object(info_cache, db_obj): + def _from_db_object(context, info_cache, db_obj): info_cache.instance_uuid = db_obj['instance_uuid'] info_cache.network_info = db_obj['network_info'] info_cache.obj_reset_changes() + info_cache._context = context return info_cache @base.remotable_classmethod def get_by_instance_uuid(cls, context, instance_uuid): db_obj = db.instance_info_cache_get(context, instance_uuid) - return InstanceInfoCache._from_db_object(cls(), db_obj) + return InstanceInfoCache._from_db_object(context, cls(), db_obj) @base.remotable def save(self, context): diff --git a/nova/tests/objects/test_objects.py b/nova/tests/objects/test_objects.py index 332833cb0..00aaf1781 100644 --- a/nova/tests/objects/test_objects.py +++ b/nova/tests/objects/test_objects.py @@ -502,3 +502,40 @@ class TestObjectListBase(test.TestCase): self.assertFalse(obj is obj2) self.assertEqual([x.foo for x in obj], [y.foo for y in obj2]) + + +class TestObjectSerializer(test.TestCase): + def test_serialize_entity_primitive(self): + ser = base.NovaObjectSerializer() + for thing in (1, 'foo', [1, 2], {'foo': 'bar'}): + self.assertEqual(thing, ser.serialize_entity(None, thing)) + + def test_deserialize_entity_primitive(self): + ser = base.NovaObjectSerializer() + for thing in (1, 'foo', [1, 2], {'foo': 'bar'}): + self.assertEqual(thing, ser.deserialize_entity(None, thing)) + + def test_object_serialization(self): + ser = base.NovaObjectSerializer() + ctxt = context.get_admin_context() + obj = MyObj() + primitive = ser.serialize_entity(ctxt, obj) + self.assertTrue('nova_object.name' in primitive) + obj2 = ser.deserialize_entity(ctxt, primitive) + self.assertTrue(isinstance(obj2, MyObj)) + self.assertEqual(ctxt, obj2._context) + + def test_object_serialization_iterables(self): + ser = base.NovaObjectSerializer() + ctxt = context.get_admin_context() + obj = MyObj() + for iterable in (list, tuple, set): + thing = iterable([obj]) + primitive = ser.serialize_entity(ctxt, thing) + self.assertEqual(1, len(primitive)) + for item in primitive: + self.assertFalse(isinstance(item, base.NovaObject)) + thing2 = ser.deserialize_entity(ctxt, primitive) + self.assertEqual(1, len(thing2)) + for item in thing2: + self.assertTrue(isinstance(item, MyObj)) |