summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Smith <danms@us.ibm.com>2013-06-18 11:01:50 -0700
committerChris Behrens <cbehrens@codestud.com>2013-06-22 21:20:57 +0000
commit92a3190128547403dc603e5a40e377c6eb0c8025 (patch)
tree1a2f81063bc97414f46cca0ef7fff0ef89a328fd
parenta9c695d82111702c562f4bb36fb9ea964b9d0913 (diff)
Fix serialization of iterable types
So far, we have not supported serialization of iterable type arguments to RPC methods. However, the recent change to pass args as a tuple instead of forcing everything to kwargs means that serialization of non-keyword args is broken. This patch adds that support, as well as tests for the serializer, which should have been there in the first place. Related to blueprint unified-object-model Change-Id: Ie02fffd89d9b79b4bc8270c14b2860c0e37d7a92
-rw-r--r--nova/objects/base.py26
-rw-r--r--nova/tests/objects/test_objects.py37
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))