Merge "Implement manifest-based backports"

This commit is contained in:
Jenkins 2015-09-03 05:08:45 +00:00 committed by Gerrit Code Review
commit 537f8a07f5
5 changed files with 85 additions and 22 deletions

View File

@ -70,6 +70,10 @@ class LocalAPI(object):
def object_backport(self, context, objinst, target_version): def object_backport(self, context, objinst, target_version):
return self._manager.object_backport(context, objinst, target_version) return self._manager.object_backport(context, objinst, target_version)
def object_backport_versions(self, context, objinst, object_versions):
return self._manager.object_backport_versions(context, objinst,
object_versions)
class LocalComputeTaskAPI(object): class LocalComputeTaskAPI(object):
def __init__(self): def __init__(self):

View File

@ -83,7 +83,7 @@ class ConductorManager(manager.Manager):
namespace. See the ComputeTaskManager class for details. namespace. See the ComputeTaskManager class for details.
""" """
target = messaging.Target(version='2.1') target = messaging.Target(version='2.2')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ConductorManager, self).__init__(service_name='conductor', super(ConductorManager, self).__init__(service_name='conductor',
@ -482,6 +482,16 @@ class ConductorManager(manager.Manager):
def object_backport(self, context, objinst, target_version): def object_backport(self, context, objinst, target_version):
return objinst.obj_to_primitive(target_version=target_version) return objinst.obj_to_primitive(target_version=target_version)
def object_backport_versions(self, context, objinst, object_versions):
target = object_versions[objinst.obj_name()]
LOG.debug('Backporting %(obj)s to %(ver)s with versions %(manifest)s',
obj=objinst.obj_name(), ver=target,
manifest=','.join(
['%s=%s' % (name, ver)
for name, ver in object_versions.items()]))
return objinst.obj_to_primitive(target_version=target,
version_manifest=object_versions)
class ComputeTaskManager(base.Base): class ComputeTaskManager(base.Base):
"""Namespace for compute methods. """Namespace for compute methods.

View File

@ -184,6 +184,7 @@ class ConductorAPI(object):
* Remove vol_usage_update() * Remove vol_usage_update()
* Remove instance_update() * Remove instance_update()
* 2.2 - Add object_backport_versions()
""" """
VERSION_ALIASES = { VERSION_ALIASES = {
@ -229,6 +230,11 @@ class ConductorAPI(object):
return cctxt.call(context, 'object_backport', objinst=objinst, return cctxt.call(context, 'object_backport', objinst=objinst,
target_version=target_version) target_version=target_version)
def object_backport_versions(self, context, objinst, object_versions):
cctxt = self.client.prepare(version='2.2')
return cctxt.call(context, 'object_backport_versions', objinst=objinst,
object_versions=object_versions)
class ComputeTaskAPI(object): class ComputeTaskAPI(object):
"""Client side of the conductor 'compute' namespaced RPC API """Client side of the conductor 'compute' namespaced RPC API

View File

@ -306,10 +306,10 @@ class NovaObjectSerializer(messaging.NoOpSerializer):
'.'.join(objver.split('.')[:2]) '.'.join(objver.split('.')[:2])
return self._process_object(context, objprim) return self._process_object(context, objprim)
objname = objprim['nova_object.name'] objname = objprim['nova_object.name']
supported = NovaObjectRegistry.obj_classes().get(objname, []) version_manifest = ovoo_base.obj_tree_get_versions(objname)
if supported: if objname in version_manifest:
objinst = self.conductor.object_backport(context, objprim, objinst = self.conductor.object_backport_versions(
supported[0].VERSION) context, objprim, version_manifest)
else: else:
raise raise
return objinst return objinst

View File

@ -25,6 +25,7 @@ import fixtures
import mock import mock
from oslo_log import log from oslo_log import log
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_versionedobjects import base as ovo_base
from oslo_versionedobjects import exception as ovo_exc from oslo_versionedobjects import exception as ovo_exc
from oslo_versionedobjects import fixture from oslo_versionedobjects import fixture
import six import six
@ -934,7 +935,7 @@ class TestObjectSerializer(_BaseTestCase):
my_version='1.6'): my_version='1.6'):
ser = base.NovaObjectSerializer() ser = base.NovaObjectSerializer()
ser._conductor = mock.Mock() ser._conductor = mock.Mock()
ser._conductor.object_backport.return_value = 'backported' ser._conductor.object_backport_versions.return_value = 'backported'
class MyTestObj(MyObj): class MyTestObj(MyObj):
VERSION = my_version VERSION = my_version
@ -946,12 +947,12 @@ class TestObjectSerializer(_BaseTestCase):
primitive = obj.obj_to_primitive() primitive = obj.obj_to_primitive()
result = ser.deserialize_entity(self.context, primitive) result = ser.deserialize_entity(self.context, primitive)
if backported_to is None: if backported_to is None:
self.assertFalse(ser._conductor.object_backport.called) self.assertFalse(ser._conductor.object_backport_versions.called)
else: else:
self.assertEqual('backported', result) self.assertEqual('backported', result)
ser._conductor.object_backport.assert_called_with(self.context, versions = ovo_base.obj_tree_get_versions('MyTestObj')
primitive, ser._conductor.object_backport_versions.assert_called_with(
backported_to) self.context, primitive, versions)
def test_deserialize_entity_newer_version_backports(self): def test_deserialize_entity_newer_version_backports(self):
self._test_deserialize_entity_newer('1.25', '1.6') self._test_deserialize_entity_newer('1.25', '1.6')
@ -983,13 +984,26 @@ class TestObjectSerializer(_BaseTestCase):
# .0 of the object. # .0 of the object.
self.assertEqual('1.6', obj.VERSION) self.assertEqual('1.6', obj.VERSION)
def test_nested_backport(self): @mock.patch('oslo_versionedobjects.base.obj_tree_get_versions')
def test_object_tree_backport(self, mock_get_versions):
# Test the full client backport path all the way from the serializer
# to the conductor and back.
self.start_service('conductor',
manager='nova.conductor.manager.ConductorManager')
# NOTE(danms): Actually register a complex set of objects,
# two versions of the same parent object which contain a
# child sub object.
@base.NovaObjectRegistry.register
class Child(base.NovaObject):
VERSION = '1.10'
@base.NovaObjectRegistry.register @base.NovaObjectRegistry.register
class Parent(base.NovaObject): class Parent(base.NovaObject):
VERSION = '1.0' VERSION = '1.0'
fields = { fields = {
'child': fields.ObjectField('MyObj'), 'child': fields.ObjectField('Child'),
} }
@base.NovaObjectRegistry.register # noqa @base.NovaObjectRegistry.register # noqa
@ -997,21 +1011,50 @@ class TestObjectSerializer(_BaseTestCase):
VERSION = '1.1' VERSION = '1.1'
fields = { fields = {
'child': fields.ObjectField('MyObj'), 'child': fields.ObjectField('Child'),
} }
child = MyObj(foo=1) # NOTE(danms): Since we're on the same node as conductor,
# return a fake version manifest so that we confirm that it
# actually honors what the client asked for and not just what
# it sees in the local machine state.
mock_get_versions.return_value = {
'Parent': '1.0',
'Child': '1.5',
}
call_context = {}
real_ofp = base.NovaObject.obj_from_primitive
def fake_obj_from_primitive(*a, **k):
# NOTE(danms): We need the first call to this to report an
# incompatible object version, but subsequent calls must
# succeed. Since we're testing the backport path all the
# way through conductor and RPC, we can't fully break this
# method, we just need it to fail once to trigger the
# backport.
if 'run' in call_context:
return real_ofp(*a, **k)
else:
call_context['run'] = True
raise ovo_exc.IncompatibleObjectVersion('foo')
child = Child()
parent = Parent(child=child) parent = Parent(child=child)
prim = parent.obj_to_primitive() prim = parent.obj_to_primitive()
child_prim = prim['nova_object.data']['child']
child_prim['nova_object.version'] = '1.10'
ser = base.NovaObjectSerializer() ser = base.NovaObjectSerializer()
with mock.patch.object(ser.conductor, 'object_backport') as backport:
ser.deserialize_entity(self.context, prim) with mock.patch('nova.objects.base.NovaObject.'
# NOTE(danms): This should be the version of the parent object, 'obj_from_primitive') as mock_ofp:
# not the child. If wrong, this will be '1.6', which is the max mock_ofp.side_effect = fake_obj_from_primitive
# child version in our registry. result = ser.deserialize_entity(self.context, prim)
backport.assert_called_once_with(self.context, prim, '1.1')
# Our newest version (and what we passed back) of Parent
# is 1.1, make sure that the manifest version is honored
self.assertEqual('1.0', result.VERSION)
# Our newest version (and what we passed back) of Child
# is 1.10, make sure that the manifest version is honored
self.assertEqual('1.5', result.child.VERSION)
def test_object_serialization(self): def test_object_serialization(self):
ser = base.NovaObjectSerializer() ser = base.NovaObjectSerializer()