diff --git a/oslo/messaging/notify/notifier.py b/oslo/messaging/notify/notifier.py index 8181558e1..ac908c019 100644 --- a/oslo/messaging/notify/notifier.py +++ b/oslo/messaging/notify/notifier.py @@ -129,6 +129,7 @@ class Notifier(object): def _notify(self, ctxt, event_type, payload, priority): payload = self._serializer.serialize_entity(ctxt, payload) + ctxt = self._serializer.serialize_context(ctxt) msg = dict(message_id=uuidutils.generate_uuid(), publisher_id=self.publisher_id, diff --git a/oslo/messaging/rpc/client.py b/oslo/messaging/rpc/client.py index e1854ed01..3d2e8963b 100644 --- a/oslo/messaging/rpc/client.py +++ b/oslo/messaging/rpc/client.py @@ -129,6 +129,8 @@ class _CallContext(object): def cast(self, ctxt, method, **kwargs): """Invoke a method and return immediately. See RPCClient.cast().""" msg = self._make_message(ctxt, method, kwargs) + ctxt = self.serializer.serialize_context(ctxt) + if self.version_cap: self._check_version_cap(msg.get('version')) try: @@ -149,6 +151,7 @@ class _CallContext(object): def call(self, ctxt, method, **kwargs): """Invoke a method and wait for a reply. See RPCClient.call().""" msg = self._make_message(ctxt, method, kwargs) + msg_ctxt = self.serializer.serialize_context(ctxt) timeout = self.timeout if self.timeout is None: @@ -160,7 +163,7 @@ class _CallContext(object): self._check_version_cap(msg.get('version')) try: - result = self.transport._send(self.target, ctxt, msg, + result = self.transport._send(self.target, msg_ctxt, msg, wait_for_reply=True, timeout=timeout) except driver_base.TransportDriverError as ex: raise ClientSendError(self.target, ex) @@ -335,6 +338,9 @@ class RPCClient(object): Method arguments must either be primitive types or types supported by the client's serializer (if any). + Similarly, the request context must be a dict unless the client's + serializer supports serializing another type. + :param ctxt: a request context dict :type ctxt: dict :param method: the method name @@ -348,7 +354,9 @@ class RPCClient(object): """Invoke a method and wait for a reply. Method arguments must either be primitive types or types supported by - the client's serializer (if any). + the client's serializer (if any). Similarly, the request context must + be a dict unless the client's serializer supports serializing another + type. The semantics of how any errors raised by the remote RPC endpoint method are handled are quite subtle. diff --git a/oslo/messaging/rpc/dispatcher.py b/oslo/messaging/rpc/dispatcher.py index bcf0864a8..183b7ca58 100644 --- a/oslo/messaging/rpc/dispatcher.py +++ b/oslo/messaging/rpc/dispatcher.py @@ -86,6 +86,7 @@ class RPCDispatcher(object): return utils.version_is_compatible(endpoint_version, version) def _dispatch(self, endpoint, method, ctxt, args): + ctxt = self.serializer.deserialize_context(ctxt) new_args = dict() for argname, arg in args.iteritems(): new_args[argname] = self.serializer.deserialize_entity(ctxt, arg) diff --git a/oslo/messaging/rpc/server.py b/oslo/messaging/rpc/server.py index 8c86ce448..690b03dff 100644 --- a/oslo/messaging/rpc/server.py +++ b/oslo/messaging/rpc/server.py @@ -86,7 +86,8 @@ supplied by the client. Parameters to the method invocation are primitive types and so must be the return values from the methods. By supplying a serializer object, a server can -deserialize arguments from - serialize return values to - primitive types. +deserialize a request context and arguments from - and serialize return values +to - primitive types. """ __all__ = [ diff --git a/oslo/messaging/serializer.py b/oslo/messaging/serializer.py index c69c70b40..d52eb831f 100644 --- a/oslo/messaging/serializer.py +++ b/oslo/messaging/serializer.py @@ -27,7 +27,7 @@ class Serializer(object): def serialize_entity(self, ctxt, entity): """Serialize something to primitive form. - :param context: Request context + :param ctxt: Request context, in deserialized form :param entity: Entity to be serialized :returns: Serialized form of entity """ @@ -36,11 +36,27 @@ class Serializer(object): def deserialize_entity(self, ctxt, entity): """Deserialize something from primitive form. - :param context: Request context + :param ctxt: Request context, in deserialized form :param entity: Primitive to be deserialized :returns: Deserialized form of entity """ + @abc.abstractmethod + def serialize_context(self, ctxt): + """Serialize a request context into a dictionary. + + :param ctxt: Request context + :returns: Serialized form of context + """ + + @abc.abstractmethod + def deserialize_context(self, ctxt): + """Deserialize a dictionary into a request context. + + :param ctxt: Request context dictionary + :returns: Deserialized form of entity + """ + class NoOpSerializer(Serializer): """A serializer that does nothing.""" @@ -50,3 +66,9 @@ class NoOpSerializer(Serializer): def deserialize_entity(self, ctxt, entity): return entity + + def serialize_context(self, ctxt): + return ctxt + + def deserialize_context(self, ctxt): + return ctxt diff --git a/tests/test_notifier.py b/tests/test_notifier.py index 4a7ed29c6..6ee2842ff 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -201,12 +201,15 @@ class TestSerializer(test_utils.BaseTestCase): timeutils.set_time_override() + self.mox.StubOutWithMock(serializer, 'serialize_context') self.mox.StubOutWithMock(serializer, 'serialize_entity') - serializer.serialize_entity({}, 'bar').AndReturn('sbar') + serializer.serialize_context(dict(user='bob')).\ + AndReturn(dict(user='alice')) + serializer.serialize_entity(dict(user='bob'), 'bar').AndReturn('sbar') self.mox.ReplayAll() - notifier.info({}, 'test.notify', 'bar') + notifier.info(dict(user='bob'), 'test.notify', 'bar') message = { 'message_id': str(message_id), @@ -217,7 +220,8 @@ class TestSerializer(test_utils.BaseTestCase): 'timestamp': str(timeutils.utcnow.override_time), } - self.assertEquals(_impl_test.NOTIFICATIONS, [({}, message, 'INFO')]) + self.assertEquals(_impl_test.NOTIFICATIONS, + [(dict(user='alice'), message, 'INFO')]) class TestLogNotifier(test_utils.BaseTestCase): diff --git a/tests/test_rpc_client.py b/tests/test_rpc_client.py index 14ddf536d..194dc57ea 100644 --- a/tests/test_rpc_client.py +++ b/tests/test_rpc_client.py @@ -295,11 +295,14 @@ class TestSerializer(test_utils.BaseTestCase): msg = dict(method='foo', args=dict([(k, 's' + v) for k, v in self.args.items()])) kwargs = dict(wait_for_reply=True, timeout=None) if self.call else {} - transport._send(messaging.Target(), self.ctxt, msg, **kwargs).\ - AndReturn(self.retval) + transport._send(messaging.Target(), + dict(user='alice'), + msg, + **kwargs).AndReturn(self.retval) self.mox.StubOutWithMock(serializer, 'serialize_entity') self.mox.StubOutWithMock(serializer, 'deserialize_entity') + self.mox.StubOutWithMock(serializer, 'serialize_context') for arg in self.args: serializer.serialize_entity(self.ctxt, arg).AndReturn('s' + arg) @@ -308,6 +311,8 @@ class TestSerializer(test_utils.BaseTestCase): serializer.deserialize_entity(self.ctxt, self.retval).\ AndReturn('d' + self.retval) + serializer.serialize_context(self.ctxt).AndReturn(dict(user='alice')) + self.mox.ReplayAll() method = client.call if self.call else client.cast diff --git a/tests/test_rpc_dispatcher.py b/tests/test_rpc_dispatcher.py index 0dbce62fe..5b367b95e 100644 --- a/tests/test_rpc_dispatcher.py +++ b/tests/test_rpc_dispatcher.py @@ -128,29 +128,33 @@ class TestSerializer(test_utils.BaseTestCase): scenarios = [ ('no_args_or_retval', - dict(ctxt={}, args={}, retval=None)), + dict(ctxt={}, dctxt={}, args={}, retval=None)), ('args_and_retval', dict(ctxt=dict(user='bob'), + dctxt=dict(user='alice'), args=dict(a='a', b='b', c='c'), retval='d')), ] def test_serializer(self): endpoint = _FakeEndpoint() - serializer = msg_serializer.NoOpSerializer + serializer = msg_serializer.NoOpSerializer() dispatcher = messaging.RPCDispatcher([endpoint], serializer) self.mox.StubOutWithMock(endpoint, 'foo') args = dict([(k, 'd' + v) for k, v in self.args.items()]) - endpoint.foo(self.ctxt, **args).AndReturn(self.retval) + endpoint.foo(self.dctxt, **args).AndReturn(self.retval) self.mox.StubOutWithMock(serializer, 'serialize_entity') self.mox.StubOutWithMock(serializer, 'deserialize_entity') + self.mox.StubOutWithMock(serializer, 'deserialize_context') + + serializer.deserialize_context(self.ctxt).AndReturn(self.dctxt) for arg in self.args: - serializer.deserialize_entity(self.ctxt, arg).AndReturn('d' + arg) + serializer.deserialize_entity(self.dctxt, arg).AndReturn('d' + arg) - serializer.serialize_entity(self.ctxt, self.retval).\ + serializer.serialize_entity(self.dctxt, self.retval).\ AndReturn('s' + self.retval if self.retval else None) self.mox.ReplayAll() diff --git a/tests/test_rpc_server.py b/tests/test_rpc_server.py index 4d28a7995..394bf99e0 100644 --- a/tests/test_rpc_server.py +++ b/tests/test_rpc_server.py @@ -51,6 +51,12 @@ class ServerSetupMixin(object): def deserialize_entity(self, ctxt, entity): return 'd' + (entity or '') + def serialize_context(self, ctxt): + return dict([(k, 's' + v) for k, v in ctxt.items()]) + + def deserialize_context(self, ctxt): + return dict([(k, 'd' + v) for k, v in ctxt.items()]) + def __init__(self): self.serializer = self.TestSerializer() @@ -254,7 +260,7 @@ class TestRPCServer(test_utils.BaseTestCase, ServerSetupMixin): self.assertEqual(client.call({'dsa': 'b'}, 'ctxt_check', key='a'), - 'dsb') + 'dsdsb') self._stop_server(client, server_thread)