From 452053e8c7d370f9ae638c1d9fdd2669f8d14eb8 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Tue, 1 Sep 2015 12:03:19 -0700 Subject: [PATCH] Teach conductor to do manifest-based object_class_action() things This adds a new object_class_action_versions() handler in conductor, much like the manifest-aware backport method recently added. Since we back-convert object results from remotable_classmethod operations, we need to receive the version manifest there as well. Since oslo.versionedobjects doesn't currently pass that, we can do a little indirection in our rpcapi to start calling the new method, gathering the manifest first. Related to blueprint liberty-bump-object-and-rpcapi-versions Change-Id: I25751edee551304ec849d7b88e42624970fef45f --- nova/conductor/manager.py | 16 +++++++++++++- nova/conductor/rpcapi.py | 20 ++++++++++++++++++ nova/tests/unit/conductor/test_conductor.py | 23 +++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index e4d03680e152..040829864864 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -83,7 +83,7 @@ class ConductorManager(manager.Manager): namespace. See the ComputeTaskManager class for details. """ - target = messaging.Target(version='2.2') + target = messaging.Target(version='2.3') def __init__(self, *args, **kwargs): super(ConductorManager, self).__init__(service_name='conductor', @@ -459,6 +459,20 @@ class ConductorManager(manager.Manager): return (result.obj_to_primitive(target_version=objver) if isinstance(result, nova_object.NovaObject) else result) + def object_class_action_versions(self, context, objname, objmethod, + object_versions, args, kwargs): + objclass = nova_object.NovaObject.obj_class_from_name( + objname, object_versions[objname]) + args = tuple([context] + list(args)) + result = self._object_dispatch(objclass, objmethod, args, kwargs) + # NOTE(danms): The RPC layer will convert to primitives for us, + # but in this case, we need to honor the version the client is + # asking for, so we do it before returning here. + return (result.obj_to_primitive( + target_version=object_versions[objname], + version_manifest=object_versions) + if isinstance(result, nova_object.NovaObject) else result) + def object_action(self, context, objinst, objmethod, args, kwargs): """Perform an action on an object.""" oldobj = objinst.obj_clone() diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py index bf8810f6b6ca..24703c22bd70 100644 --- a/nova/conductor/rpcapi.py +++ b/nova/conductor/rpcapi.py @@ -18,6 +18,7 @@ from oslo_config import cfg import oslo_messaging as messaging from oslo_serialization import jsonutils +from oslo_versionedobjects import base as ovo_base from nova.objects import base as objects_base from nova import rpc @@ -185,6 +186,7 @@ class ConductorAPI(object): * Remove instance_update() * 2.2 - Add object_backport_versions() + * 2.3 - Add object_class_action_versions() """ VERSION_ALIASES = { @@ -215,11 +217,29 @@ class ConductorAPI(object): def object_class_action(self, context, objname, objmethod, objver, args, kwargs): + if self.client.can_send_version('2.3'): + # NOTE(danms): If we're new enough, collect the object + # version manifest and redirect the call to the newer + # class action handler + versions = ovo_base.obj_tree_get_versions(objname) + return self.object_class_action_versions(context, + objname, + objmethod, + versions, + args, kwargs) cctxt = self.client.prepare() return cctxt.call(context, 'object_class_action', objname=objname, objmethod=objmethod, objver=objver, args=args, kwargs=kwargs) + def object_class_action_versions(self, context, objname, objmethod, + object_versions, args, kwargs): + cctxt = self.client.prepare(version='2.3') + return cctxt.call(context, 'object_class_action_versions', + objname=objname, objmethod=objmethod, + object_versions=object_versions, + args=args, kwargs=kwargs) + def object_action(self, context, objinst, objmethod, args, kwargs): cctxt = self.client.prepare() return cctxt.call(context, 'object_action', objinst=objinst, diff --git a/nova/tests/unit/conductor/test_conductor.py b/nova/tests/unit/conductor/test_conductor.py index 174cd0cd6fb8..e28a30fdb5fb 100644 --- a/nova/tests/unit/conductor/test_conductor.py +++ b/nova/tests/unit/conductor/test_conductor.py @@ -395,6 +395,29 @@ class ConductorTestCase(_BaseTestCase, test.TestCase): self.assertIn('dict', updates) self.assertEqual({'foo': 'bar'}, updates['dict']) + def test_object_class_action_versions(self): + @obj_base.NovaObjectRegistry.register + class TestObject(obj_base.NovaObject): + VERSION = '1.10' + + @classmethod + def foo(cls, context): + return cls() + + versions = { + 'TestObject': '1.2', + 'OtherObj': '1.0', + } + with mock.patch.object(self.conductor_manager, + '_object_dispatch') as m: + m.return_value = TestObject() + m.return_value.obj_to_primitive = mock.MagicMock() + self.conductor.object_class_action_versions( + self.context, TestObject.obj_name(), 'foo', versions, + tuple(), {}) + m.return_value.obj_to_primitive.assert_called_once_with( + target_version='1.2', version_manifest=versions) + def _test_expected_exceptions(self, db_method, conductor_method, errors, *args, **kwargs): # Tests that expected exceptions are handled properly.