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
This commit is contained in:
Dan Smith 2015-09-01 12:03:19 -07:00
parent 638a807686
commit 452053e8c7
3 changed files with 58 additions and 1 deletions

View File

@ -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()

View File

@ -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,

View File

@ -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.