diff options
-rw-r--r-- | nova/objects/base.py | 26 | ||||
-rw-r--r-- | nova/tests/objects/test_objects.py | 37 |
2 files changed, 61 insertions, 2 deletions
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/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)) |