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):
|
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):
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue