summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Lindgren <hanlind@kth.se>2016-06-29 20:29:47 +0200
committerHans Lindgren <hanlind@kth.se>2016-07-02 13:20:28 +0200
commit1e3e7309997f90fbd0291c05cc859dd9ac0ef161 (patch)
treeae421419c07251215746ea1c96b9142dbe7d8294
parentceaacaa24fb50d484d4434915f3943fd9d97642a (diff)
Do not try to backport when db has older object version
Instance extras are stored as serialized objects in the database and because of this it is possible to get a version back that is older than what the client requested. This is in itself not a problem, but the way we do things right now in conductor we end up trying to backport to a newer version, which raises InvalidTargetVersion. This change adds a check to make sure we skip backporting if the requested version is newer than the actual db version as long as the major version is the same. Change-Id: I34ac0abd016b72d585f83ae2ce34790751082180 Closes-Bug: #1596119
Notes
Notes (review): Verified+1: DB Datasets CI <turbo-hipster@lists.rcbops.com> Verified+1: Citrix XenServer CI Code-Review+1: Erik Olof Gunnar Andersson <eandersson@blizzard.com> Code-Review+2: Dan Smith <dms@danplanet.com> Code-Review+2: John Garbutt <john@johngarbutt.com> Workflow+1: John Garbutt <john@johngarbutt.com> Verified+2: Jenkins Submitted-by: Jenkins Submitted-at: Tue, 20 Sep 2016 10:58:11 +0000 Reviewed-on: https://review.openstack.org/335629 Project: openstack/nova Branch: refs/heads/master
-rw-r--r--nova/conductor/manager.py20
-rw-r--r--nova/tests/unit/conductor/test_conductor.py50
2 files changed, 66 insertions, 4 deletions
diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py
index adb5f45..badb11e 100644
--- a/nova/conductor/manager.py
+++ b/nova/conductor/manager.py
@@ -20,6 +20,7 @@ from oslo_config import cfg
20from oslo_log import log as logging 20from oslo_log import log as logging
21import oslo_messaging as messaging 21import oslo_messaging as messaging
22from oslo_utils import excutils 22from oslo_utils import excutils
23from oslo_utils import versionutils
23import six 24import six
24 25
25from nova.compute import rpcapi as compute_rpcapi 26from nova.compute import rpcapi as compute_rpcapi
@@ -96,10 +97,21 @@ class ConductorManager(manager.Manager):
96 # NOTE(danms): The RPC layer will convert to primitives for us, 97 # NOTE(danms): The RPC layer will convert to primitives for us,
97 # but in this case, we need to honor the version the client is 98 # but in this case, we need to honor the version the client is
98 # asking for, so we do it before returning here. 99 # asking for, so we do it before returning here.
99 return (result.obj_to_primitive( 100 # NOTE(hanlind): Do not convert older than requested objects,
100 target_version=object_versions[objname], 101 # see bug #1596119.
101 version_manifest=object_versions) 102 if isinstance(result, nova_object.NovaObject):
102 if isinstance(result, nova_object.NovaObject) else result) 103 target_version = object_versions[objname]
104 requested_version = versionutils.convert_version_to_tuple(
105 target_version)
106 actual_version = versionutils.convert_version_to_tuple(
107 result.VERSION)
108 do_backport = requested_version < actual_version
109 other_major_version = requested_version[0] != actual_version[0]
110 if do_backport or other_major_version:
111 result = result.obj_to_primitive(
112 target_version=target_version,
113 version_manifest=object_versions)
114 return result
103 115
104 def object_action(self, context, objinst, objmethod, args, kwargs): 116 def object_action(self, context, objinst, objmethod, args, kwargs):
105 """Perform an action on an object.""" 117 """Perform an action on an object."""
diff --git a/nova/tests/unit/conductor/test_conductor.py b/nova/tests/unit/conductor/test_conductor.py
index 788efa5..41ee633 100644
--- a/nova/tests/unit/conductor/test_conductor.py
+++ b/nova/tests/unit/conductor/test_conductor.py
@@ -22,6 +22,7 @@ import mock
22from mox3 import mox 22from mox3 import mox
23import oslo_messaging as messaging 23import oslo_messaging as messaging
24from oslo_utils import timeutils 24from oslo_utils import timeutils
25from oslo_versionedobjects import exception as ovo_exc
25import six 26import six
26 27
27from nova.compute import flavors 28from nova.compute import flavors
@@ -184,6 +185,55 @@ class ConductorTestCase(_BaseTestCase, test.TestCase):
184 m.return_value.obj_to_primitive.assert_called_once_with( 185 m.return_value.obj_to_primitive.assert_called_once_with(
185 target_version='1.2', version_manifest=versions) 186 target_version='1.2', version_manifest=versions)
186 187
188 def test_object_class_action_versions_old_object(self):
189 # Make sure we return older than requested objects unmodified,
190 # see bug #1596119.
191 @obj_base.NovaObjectRegistry.register
192 class TestObject(obj_base.NovaObject):
193 VERSION = '1.10'
194
195 @classmethod
196 def foo(cls, context):
197 return cls()
198
199 versions = {
200 'TestObject': '1.10',
201 'OtherObj': '1.0',
202 }
203 with mock.patch.object(self.conductor_manager,
204 '_object_dispatch') as m:
205 m.return_value = TestObject()
206 m.return_value.VERSION = '1.9'
207 m.return_value.obj_to_primitive = mock.MagicMock()
208 obj = self.conductor.object_class_action_versions(
209 self.context, TestObject.obj_name(), 'foo', versions,
210 tuple(), {})
211 self.assertFalse(m.return_value.obj_to_primitive.called)
212 self.assertEqual('1.9', obj.VERSION)
213
214 def test_object_class_action_versions_major_version_diff(self):
215 @obj_base.NovaObjectRegistry.register
216 class TestObject(obj_base.NovaObject):
217 VERSION = '2.10'
218
219 @classmethod
220 def foo(cls, context):
221 return cls()
222
223 versions = {
224 'TestObject': '2.10',
225 'OtherObj': '1.0',
226 }
227 with mock.patch.object(self.conductor_manager,
228 '_object_dispatch') as m:
229 m.return_value = TestObject()
230 m.return_value.VERSION = '1.9'
231 self.assertRaises(
232 ovo_exc.InvalidTargetVersion,
233 self.conductor.object_class_action_versions,
234 self.context, TestObject.obj_name(), 'foo', versions,
235 tuple(), {})
236
187 def test_reset(self): 237 def test_reset(self):
188 with mock.patch.object(objects.Service, 'clear_min_version_cache' 238 with mock.patch.object(objects.Service, 'clear_min_version_cache'
189 ) as mock_clear_cache: 239 ) as mock_clear_cache: