Merge "Implement manifest-based backports"
This commit is contained in:
commit
537f8a07f5
|
@ -70,6 +70,10 @@ class LocalAPI(object):
|
|||
def object_backport(self, 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):
|
||||
def __init__(self):
|
||||
|
|
|
@ -83,7 +83,7 @@ class ConductorManager(manager.Manager):
|
|||
namespace. See the ComputeTaskManager class for details.
|
||||
"""
|
||||
|
||||
target = messaging.Target(version='2.1')
|
||||
target = messaging.Target(version='2.2')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ConductorManager, self).__init__(service_name='conductor',
|
||||
|
@ -482,6 +482,16 @@ class ConductorManager(manager.Manager):
|
|||
def object_backport(self, context, objinst, 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):
|
||||
"""Namespace for compute methods.
|
||||
|
|
|
@ -184,6 +184,7 @@ class ConductorAPI(object):
|
|||
* Remove vol_usage_update()
|
||||
* Remove instance_update()
|
||||
|
||||
* 2.2 - Add object_backport_versions()
|
||||
"""
|
||||
|
||||
VERSION_ALIASES = {
|
||||
|
@ -229,6 +230,11 @@ class ConductorAPI(object):
|
|||
return cctxt.call(context, 'object_backport', objinst=objinst,
|
||||
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):
|
||||
"""Client side of the conductor 'compute' namespaced RPC API
|
||||
|
|
|
@ -306,10 +306,10 @@ class NovaObjectSerializer(messaging.NoOpSerializer):
|
|||
'.'.join(objver.split('.')[:2])
|
||||
return self._process_object(context, objprim)
|
||||
objname = objprim['nova_object.name']
|
||||
supported = NovaObjectRegistry.obj_classes().get(objname, [])
|
||||
if supported:
|
||||
objinst = self.conductor.object_backport(context, objprim,
|
||||
supported[0].VERSION)
|
||||
version_manifest = ovoo_base.obj_tree_get_versions(objname)
|
||||
if objname in version_manifest:
|
||||
objinst = self.conductor.object_backport_versions(
|
||||
context, objprim, version_manifest)
|
||||
else:
|
||||
raise
|
||||
return objinst
|
||||
|
|
|
@ -25,6 +25,7 @@ import fixtures
|
|||
import mock
|
||||
from oslo_log import log
|
||||
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 fixture
|
||||
import six
|
||||
|
@ -934,7 +935,7 @@ class TestObjectSerializer(_BaseTestCase):
|
|||
my_version='1.6'):
|
||||
ser = base.NovaObjectSerializer()
|
||||
ser._conductor = mock.Mock()
|
||||
ser._conductor.object_backport.return_value = 'backported'
|
||||
ser._conductor.object_backport_versions.return_value = 'backported'
|
||||
|
||||
class MyTestObj(MyObj):
|
||||
VERSION = my_version
|
||||
|
@ -946,12 +947,12 @@ class TestObjectSerializer(_BaseTestCase):
|
|||
primitive = obj.obj_to_primitive()
|
||||
result = ser.deserialize_entity(self.context, primitive)
|
||||
if backported_to is None:
|
||||
self.assertFalse(ser._conductor.object_backport.called)
|
||||
self.assertFalse(ser._conductor.object_backport_versions.called)
|
||||
else:
|
||||
self.assertEqual('backported', result)
|
||||
ser._conductor.object_backport.assert_called_with(self.context,
|
||||
primitive,
|
||||
backported_to)
|
||||
versions = ovo_base.obj_tree_get_versions('MyTestObj')
|
||||
ser._conductor.object_backport_versions.assert_called_with(
|
||||
self.context, primitive, versions)
|
||||
|
||||
def test_deserialize_entity_newer_version_backports(self):
|
||||
self._test_deserialize_entity_newer('1.25', '1.6')
|
||||
|
@ -983,13 +984,26 @@ class TestObjectSerializer(_BaseTestCase):
|
|||
# .0 of the object.
|
||||
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
|
||||
class Parent(base.NovaObject):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'child': fields.ObjectField('MyObj'),
|
||||
'child': fields.ObjectField('Child'),
|
||||
}
|
||||
|
||||
@base.NovaObjectRegistry.register # noqa
|
||||
|
@ -997,21 +1011,50 @@ class TestObjectSerializer(_BaseTestCase):
|
|||
VERSION = '1.1'
|
||||
|
||||
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)
|
||||
prim = parent.obj_to_primitive()
|
||||
child_prim = prim['nova_object.data']['child']
|
||||
child_prim['nova_object.version'] = '1.10'
|
||||
ser = base.NovaObjectSerializer()
|
||||
with mock.patch.object(ser.conductor, 'object_backport') as backport:
|
||||
ser.deserialize_entity(self.context, prim)
|
||||
# NOTE(danms): This should be the version of the parent object,
|
||||
# not the child. If wrong, this will be '1.6', which is the max
|
||||
# child version in our registry.
|
||||
backport.assert_called_once_with(self.context, prim, '1.1')
|
||||
|
||||
with mock.patch('nova.objects.base.NovaObject.'
|
||||
'obj_from_primitive') as mock_ofp:
|
||||
mock_ofp.side_effect = fake_obj_from_primitive
|
||||
result = ser.deserialize_entity(self.context, prim)
|
||||
|
||||
# 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):
|
||||
ser = base.NovaObjectSerializer()
|
||||
|
|
Loading…
Reference in New Issue