diff --git a/cinder/exception.py b/cinder/exception.py index ecc256ead8c..ef0462fdee0 100644 --- a/cinder/exception.py +++ b/cinder/exception.py @@ -774,6 +774,11 @@ ObjectActionError = obj_exc.ObjectActionError ObjectFieldInvalid = obj_exc.ObjectFieldInvalid +class CappedVersionUnknown(CinderException): + message = _('Unrecoverable Error: Versioned Objects in DB are capped to ' + 'unknown version %(version)s.') + + class VolumeGroupNotFound(CinderException): message = _('Unable to find Volume Group: %(vg_name)s') diff --git a/cinder/objects/backup.py b/cinder/objects/backup.py index f16abc004de..7f09800fa2a 100644 --- a/cinder/objects/backup.py +++ b/cinder/objects/backup.py @@ -162,9 +162,6 @@ class BackupList(base.ObjectListBase, base.CinderObject): fields = { 'objects': fields.ListOfObjectsField('Backup'), } - child_versions = { - '1.0': '1.0' - } @base.remotable_classmethod def get_all(cls, context, filters=None, marker=None, limit=None, diff --git a/cinder/objects/base.py b/cinder/objects/base.py index af5da4796d3..8495158f12c 100644 --- a/cinder/objects/base.py +++ b/cinder/objects/base.py @@ -411,6 +411,17 @@ class CinderObjectSerializer(base.VersionedObjectSerializer): super(CinderObjectSerializer, self).__init__() self.version_cap = version_cap + # NOTE(geguileo): During upgrades we will use a manifest to ensure that + # all objects are properly backported. This allows us to properly + # backport child objects to the right version even if parent version + # has not been bumped. + if not version_cap or version_cap == OBJ_VERSIONS.get_current(): + self.manifest = None + else: + if version_cap not in OBJ_VERSIONS: + raise exception.CappedVersionUnknown(version=version_cap) + self.manifest = OBJ_VERSIONS[version_cap] + def _get_capped_obj_version(self, obj): objname = obj.obj_name() version_dict = OBJ_VERSIONS.get(self.version_cap, {}) @@ -436,5 +447,5 @@ class CinderObjectSerializer(base.VersionedObjectSerializer): callable(entity.obj_to_primitive)): # NOTE(dulek): Backport outgoing object to the capped version. backport_ver = self._get_capped_obj_version(entity) - entity = entity.obj_to_primitive(backport_ver) + entity = entity.obj_to_primitive(backport_ver, self.manifest) return entity diff --git a/cinder/objects/cgsnapshot.py b/cinder/objects/cgsnapshot.py index 1da32250645..d7b7ba6b7a3 100644 --- a/cinder/objects/cgsnapshot.py +++ b/cinder/objects/cgsnapshot.py @@ -127,9 +127,6 @@ class CGSnapshotList(base.ObjectListBase, base.CinderObject): fields = { 'objects': fields.ListOfObjectsField('CGSnapshot') } - child_version = { - '1.0': '1.0' - } @base.remotable_classmethod def get_all(cls, context, filters=None): diff --git a/cinder/objects/consistencygroup.py b/cinder/objects/consistencygroup.py index f6d1ab4f6e5..874de8aa881 100644 --- a/cinder/objects/consistencygroup.py +++ b/cinder/objects/consistencygroup.py @@ -151,10 +151,6 @@ class ConsistencyGroupList(base.ObjectListBase, base.CinderObject): fields = { 'objects': fields.ListOfObjectsField('ConsistencyGroup') } - child_version = { - '1.0': '1.0', - '1.1': '1.1', - } @base.remotable_classmethod def get_all(cls, context, filters=None, marker=None, limit=None, diff --git a/cinder/objects/service.py b/cinder/objects/service.py index 9bf2fdc5aa5..237d3860955 100644 --- a/cinder/objects/service.py +++ b/cinder/objects/service.py @@ -139,10 +139,6 @@ class ServiceList(base.ObjectListBase, base.CinderObject): fields = { 'objects': fields.ListOfObjectsField('Service'), } - child_versions = { - '1.0': '1.0', - '1.1': '1.2', - } @base.remotable_classmethod def get_all(cls, context, filters=None): diff --git a/cinder/objects/snapshot.py b/cinder/objects/snapshot.py index 08d4b48efd8..18f93e2b378 100644 --- a/cinder/objects/snapshot.py +++ b/cinder/objects/snapshot.py @@ -226,9 +226,6 @@ class SnapshotList(base.ObjectListBase, base.CinderObject): fields = { 'objects': fields.ListOfObjectsField('Snapshot'), } - child_versions = { - '1.0': '1.0' - } @base.remotable_classmethod def get_all(cls, context, search_opts, marker=None, limit=None, diff --git a/cinder/objects/volume.py b/cinder/objects/volume.py index b4fb393f72e..d14d9297cda 100644 --- a/cinder/objects/volume.py +++ b/cinder/objects/volume.py @@ -441,11 +441,6 @@ class VolumeList(base.ObjectListBase, base.CinderObject): 'objects': fields.ListOfObjectsField('Volume'), } - child_versions = { - '1.0': '1.0', - '1.1': '1.1', - } - @classmethod def _get_expected_attrs(cls, context): expected_attrs = ['metadata', 'volume_type'] diff --git a/cinder/objects/volume_attachment.py b/cinder/objects/volume_attachment.py index 8e809b248b0..4c2e0e19e4f 100644 --- a/cinder/objects/volume_attachment.py +++ b/cinder/objects/volume_attachment.py @@ -68,10 +68,6 @@ class VolumeAttachmentList(base.ObjectListBase, base.CinderObject): 'objects': fields.ListOfObjectsField('VolumeAttachment'), } - child_versions = { - '1.0': '1.0', - } - @base.remotable_classmethod def get_all_by_volume_id(cls, context, volume_id): attachments = db.volume_attachment_get_all_by_volume_id(context, diff --git a/cinder/objects/volume_type.py b/cinder/objects/volume_type.py index 546c551788b..b8f5d4857d1 100644 --- a/cinder/objects/volume_type.py +++ b/cinder/objects/volume_type.py @@ -107,11 +107,6 @@ class VolumeTypeList(base.ObjectListBase, base.CinderObject): 'objects': fields.ListOfObjectsField('VolumeType'), } - child_versions = { - '1.0': '1.0', - '1.1': '1.0', - } - @base.remotable_classmethod def get_all(cls, context, inactive=0, filters=None, marker=None, limit=None, sort_keys=None, sort_dirs=None, offset=None): diff --git a/cinder/tests/unit/backup/test_backup.py b/cinder/tests/unit/backup/test_backup.py index d2b93145cca..b35c0933184 100644 --- a/cinder/tests/unit/backup/test_backup.py +++ b/cinder/tests/unit/backup/test_backup.py @@ -301,19 +301,21 @@ class BackupTestCase(BaseBackupTest): @mock.patch('cinder.objects.service.Service.get_minimum_obj_version') @mock.patch('cinder.rpc.LAST_RPC_VERSIONS', {'cinder-backup': '1.3', 'cinder-volume': '1.7'}) - @mock.patch('cinder.rpc.LAST_OBJ_VERSIONS', {'cinder-backup': '1.5', + @mock.patch('cinder.rpc.LAST_OBJ_VERSIONS', {'cinder-backup': '1.2', 'cinder-volume': '1.4'}) def test_reset(self, get_min_obj, get_min_rpc): + get_min_obj.return_value = 'liberty' backup_mgr = manager.BackupManager() backup_rpcapi = backup_mgr.backup_rpcapi volume_rpcapi = backup_mgr.volume_rpcapi self.assertEqual('1.3', backup_rpcapi.client.version_cap) - self.assertEqual('1.5', + self.assertEqual('1.2', backup_rpcapi.client.serializer._base.version_cap) self.assertEqual('1.7', volume_rpcapi.client.version_cap) self.assertEqual('1.4', volume_rpcapi.client.serializer._base.version_cap) + get_min_obj.return_value = objects.base.OBJ_VERSIONS.get_current() backup_mgr.reset() backup_rpcapi = backup_mgr.backup_rpcapi @@ -322,10 +324,12 @@ class BackupTestCase(BaseBackupTest): backup_rpcapi.client.version_cap) self.assertEqual(get_min_obj.return_value, backup_rpcapi.client.serializer._base.version_cap) + self.assertIsNone(backup_rpcapi.client.serializer._base.manifest) self.assertEqual(get_min_rpc.return_value, volume_rpcapi.client.version_cap) self.assertEqual(get_min_obj.return_value, volume_rpcapi.client.serializer._base.version_cap) + self.assertIsNone(volume_rpcapi.client.serializer._base.manifest) def test_is_working(self): self.assertTrue(self.backup_mgr.is_working()) diff --git a/cinder/tests/unit/fake_objects.py b/cinder/tests/unit/fake_objects.py new file mode 100644 index 00000000000..e1787ff0dfc --- /dev/null +++ b/cinder/tests/unit/fake_objects.py @@ -0,0 +1,81 @@ +# Copyright (c) 2016 Red Hat Inc. +# Copyright (c) 2016 Intel Corp. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_utils import versionutils + +from cinder import objects + + +@objects.base.CinderObjectRegistry.register_if(False) +class ChildObject(objects.base.CinderObject): + VERSION = '1.2' + + fields = { + 'scheduled_at': objects.base.fields.DateTimeField(nullable=True), + 'uuid': objects.base.fields.UUIDField(), + 'text': objects.base.fields.StringField(nullable=True), + 'integer': objects.base.fields.IntegerField(nullable=True), + } + + def obj_make_compatible(self, primitive, target_version): + super(ChildObject, self).obj_make_compatible(primitive, + target_version) + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + primitive.pop('text', None) + if target_version < (1, 2): + primitive.pop('integer', None) + + +@objects.base.CinderObjectRegistry.register_if(False) +class ParentObject(objects.base.CinderObject): + VERSION = '1.1' + + fields = { + 'uuid': objects.base.fields.UUIDField(), + 'child': objects.base.fields.ObjectField('ChildObject', nullable=True), + 'scheduled_at': objects.base.fields.DateTimeField(nullable=True), + } + + def obj_make_compatible(self, primitive, target_version): + super(ParentObject, self).obj_make_compatible(primitive, + target_version) + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + primitive.pop('scheduled_at', None) + + +@objects.base.CinderObjectRegistry.register_if(False) +class ParentObjectList(objects.base.CinderObject, objects.base.ObjectListBase): + VERSION = ParentObject.VERSION + + fields = { + 'objects': objects.base.fields.ListOfObjectsField('ParentObject'), + } + + +class MyHistory(objects.base.CinderObjectVersionsHistory): + linked_objects = {'ParentObject': 'ParentObjectList'} + + def __init__(self): + self.versions = ['1.0'] + self['1.0'] = {'ChildObject': '1.0'} + self.add('1.1', {'ChildObject': '1.1'}) + self.add('1.2', {'ParentObject': '1.0'}) + self.add('1.3', {'ParentObjectList': '1.0'}) + self.add('1.4', {'ParentObject': '1.1'}) + self.add('1.5', {'ParentObjectList': '1.1'}) + self.add('1.6', {'ChildObject': '1.2'}) diff --git a/cinder/tests/unit/objects/test_base.py b/cinder/tests/unit/objects/test_base.py index 0eb805f6ac7..e4546a38b12 100644 --- a/cinder/tests/unit/objects/test_base.py +++ b/cinder/tests/unit/objects/test_base.py @@ -17,7 +17,6 @@ import uuid from iso8601 import iso8601 import mock -from oslo_utils import versionutils from oslo_versionedobjects import fields from sqlalchemy import sql @@ -28,33 +27,16 @@ from cinder import exception from cinder import objects from cinder import test from cinder.tests.unit import fake_constants as fake +from cinder.tests.unit import fake_objects from cinder.tests.unit import objects as test_objects -@objects.base.CinderObjectRegistry.register_if(False) -class TestObject(objects.base.CinderObject): - VERSION = '1.1' - - fields = { - 'scheduled_at': objects.base.fields.DateTimeField(nullable=True), - 'uuid': objects.base.fields.UUIDField(), - 'text': objects.base.fields.StringField(nullable=True), - } - - def obj_make_compatible(self, primitive, target_version): - super(TestObject, self).obj_make_compatible(primitive, - target_version) - target_version = versionutils.convert_version_to_tuple(target_version) - if target_version < (1, 1): - primitive.pop('text', None) - - class TestCinderObject(test_objects.BaseObjectsTestCase): """Tests methods from CinderObject.""" def setUp(self): super(TestCinderObject, self).setUp() - self.obj = TestObject( + self.obj = fake_objects.ChildObject( scheduled_at=None, uuid=uuid.uuid4(), text='text') @@ -720,21 +702,107 @@ class TestCinderDictObject(test_objects.BaseObjectsTestCase): self.assertFalse('def' in obj) -@mock.patch('cinder.objects.base.OBJ_VERSIONS', {'1.0': {'TestObject': '1.0'}, - '1.1': {'TestObject': '1.1'}, - }) +@mock.patch('cinder.objects.base.OBJ_VERSIONS', fake_objects.MyHistory()) class TestCinderObjectSerializer(test_objects.BaseObjectsTestCase): def setUp(self): super(TestCinderObjectSerializer, self).setUp() - self.obj = TestObject(scheduled_at=None, uuid=uuid.uuid4(), - text='text') + self.obj = fake_objects.ChildObject(scheduled_at=None, + uuid=uuid.uuid4(), + text='text', + integer=1) + self.parent = fake_objects.ParentObject(uuid=uuid.uuid4(), + child=self.obj, + scheduled_at=None) + self.parent_list = fake_objects.ParentObjectList(objects=[self.parent]) - def test_serialize_entity_backport(self): - serializer = objects.base.CinderObjectSerializer('1.0') - primitive = serializer.serialize_entity(self.context, self.obj) - self.assertEqual('1.0', primitive['versioned_object.version']) + def test_serialize_init_current_has_no_manifest(self): + """Test that pinned to current version we have no manifest.""" + serializer = objects.base.CinderObjectSerializer('1.6') + # Serializer should not have a manifest + self.assertIsNone(serializer.manifest) + + def test_serialize_init_no_cap_has_no_manifest(self): + """Test that without cap we have no manifest.""" + serializer = objects.base.CinderObjectSerializer() + # Serializer should not have a manifest + self.assertIsNone(serializer.manifest) + + def test_serialize_init_pinned_has_manifest(self): + """Test that pinned to older version we have manifest.""" + objs_version = '1.5' + serializer = objects.base.CinderObjectSerializer(objs_version) + # Serializer should have the right manifest + self.assertDictEqual(fake_objects.MyHistory()[objs_version], + serializer.manifest) def test_serialize_entity_unknown_version(self): - serializer = objects.base.CinderObjectSerializer('0.9') + """Test that bad cap version will prevent serializer creation.""" + self.assertRaises(exception.CappedVersionUnknown, + objects.base.CinderObjectSerializer, '0.9') + + def test_serialize_entity_basic_no_backport(self): + """Test single element serializer with no backport.""" + serializer = objects.base.CinderObjectSerializer('1.6') + primitive = serializer.serialize_entity(self.context, self.obj) + self.assertEqual('1.2', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual(1, data['integer']) + self.assertEqual('text', data['text']) + + def test_serialize_entity_basic_backport(self): + """Test single element serializer with backport.""" + serializer = objects.base.CinderObjectSerializer('1.5') primitive = serializer.serialize_entity(self.context, self.obj) self.assertEqual('1.1', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertNotIn('integer', data) + self.assertEqual('text', data['text']) + + def test_serialize_entity_full_no_backport(self): + """Test related elements serialization with no backport.""" + serializer = objects.base.CinderObjectSerializer('1.6') + primitive = serializer.serialize_entity(self.context, self.parent_list) + self.assertEqual('1.1', primitive['versioned_object.version']) + parent = primitive['versioned_object.data']['objects'][0] + self.assertEqual('1.1', parent['versioned_object.version']) + child = parent['versioned_object.data']['child'] + self.assertEqual('1.2', child['versioned_object.version']) + + def test_serialize_entity_full_backport_last_children(self): + """Test related elements serialization with backport of the last child. + + Test that using the manifest we properly backport a child object even + when all its parents have not changed their version. + """ + serializer = objects.base.CinderObjectSerializer('1.5') + primitive = serializer.serialize_entity(self.context, self.parent_list) + self.assertEqual('1.1', primitive['versioned_object.version']) + parent = primitive['versioned_object.data']['objects'][0] + self.assertEqual('1.1', parent['versioned_object.version']) + # Only the child has been backported + child = parent['versioned_object.data']['child'] + self.assertEqual('1.1', child['versioned_object.version']) + # Check that the backport has been properly done + data = child['versioned_object.data'] + self.assertNotIn('integer', data) + self.assertEqual('text', data['text']) + + def test_serialize_entity_full_backport(self): + """Test backport of the whole tree of related elements.""" + serializer = objects.base.CinderObjectSerializer('1.3') + primitive = serializer.serialize_entity(self.context, self.parent_list) + # List has been backported + self.assertEqual('1.0', primitive['versioned_object.version']) + parent = primitive['versioned_object.data']['objects'][0] + # Parent has been backported as well + self.assertEqual('1.0', parent['versioned_object.version']) + # And the backport has been properly done + data = parent['versioned_object.data'] + self.assertNotIn('scheduled_at', data) + # And child as well + child = parent['versioned_object.data']['child'] + self.assertEqual('1.1', child['versioned_object.version']) + # Check that the backport has been properly done + data = child['versioned_object.data'] + self.assertNotIn('integer', data) + self.assertEqual('text', data['text']) diff --git a/cinder/tests/unit/objects/test_objects.py b/cinder/tests/unit/objects/test_objects.py index 0cb386a06b8..1b44924e791 100644 --- a/cinder/tests/unit/objects/test_objects.py +++ b/cinder/tests/unit/objects/test_objects.py @@ -25,21 +25,21 @@ from cinder import test object_data = { 'Backup': '1.4-bcd1797dc2f3e17a46571525e9dbec30', 'BackupImport': '1.4-bcd1797dc2f3e17a46571525e9dbec30', - 'BackupList': '1.0-24591dabe26d920ce0756fe64cd5f3aa', + 'BackupList': '1.0-7350483276ddb74960c8c39b69192eaa', 'CGSnapshot': '1.0-de2586a31264d7647f40c762dece9d58', 'CGSnapshotList': '1.0-e8c3f4078cd0ee23487b34d173eec776', 'ConsistencyGroup': '1.2-de280886bd04d7e3184c1f7c3a7e2074', 'ConsistencyGroupList': '1.1-73916823b697dfa0c7f02508d87e0f28', 'Service': '1.3-66c8e1683f58546c54551e9ff0a3b111', - 'ServiceList': '1.1-cb758b200f0a3a90efabfc5aa2ffb627', + 'ServiceList': '1.1-07d2be494d704784ad2af4d4c91e68e5', 'Snapshot': '1.1-ac41f2fe2fb0e34127155d1ec6e4c7e0', - 'SnapshotList': '1.0-71661e7180ef6cc51501704a9bea4bf1', + 'SnapshotList': '1.0-58441afd20ddf9417b8879aa6de1ee6f', 'Volume': '1.3-049e3e5dc411b1a4deb7d6ee4f1ad5ef', + 'VolumeList': '1.1-8859f973dc02e9eb4582063a171bd0f1', 'VolumeAttachment': '1.0-8fc9a9ac6f554fdf2a194d25dbf28a3b', - 'VolumeAttachmentList': '1.0-307d2b6c8dd55ef854f6386898e9e98e', - 'VolumeList': '1.1-03ba6cb8c546683e64e15c50042cb1a3', + 'VolumeAttachmentList': '1.0-4ef79b3824e5d1717ebe0d0558ddff96', 'VolumeType': '1.0-dd980cfd1eef2dcce941a981eb469fc8', - 'VolumeTypeList': '1.1-8a1016c03570dc13b9a33fe04a6acb2c', + 'VolumeTypeList': '1.1-68a4549e98563caec82a2018638fa69c', } diff --git a/cinder/tests/unit/scheduler/test_scheduler.py b/cinder/tests/unit/scheduler/test_scheduler.py index dc2fae78637..c74dbffd600 100644 --- a/cinder/tests/unit/scheduler/test_scheduler.py +++ b/cinder/tests/unit/scheduler/test_scheduler.py @@ -24,6 +24,7 @@ from cinder import context from cinder import db from cinder import exception from cinder.message import defined_messages +from cinder import objects from cinder.objects import fields from cinder.scheduler import driver from cinder.scheduler import filter_scheduler @@ -74,14 +75,15 @@ class SchedulerManagerTestCase(test.TestCase): @mock.patch('cinder.objects.service.Service.get_minimum_rpc_version') @mock.patch('cinder.objects.service.Service.get_minimum_obj_version') @mock.patch('cinder.rpc.LAST_RPC_VERSIONS', {'cinder-volume': '1.3'}) - @mock.patch('cinder.rpc.LAST_OBJ_VERSIONS', {'cinder-volume': '1.5'}) + @mock.patch('cinder.rpc.LAST_OBJ_VERSIONS', {'cinder-volume': '1.4'}) def test_reset(self, get_min_obj, get_min_rpc): mgr = self.manager_cls() volume_rpcapi = mgr.driver.volume_rpcapi self.assertEqual('1.3', volume_rpcapi.client.version_cap) - self.assertEqual('1.5', + self.assertEqual('1.4', volume_rpcapi.client.serializer._base.version_cap) + get_min_obj.return_value = objects.base.OBJ_VERSIONS.get_current() mgr.reset() volume_rpcapi = mgr.driver.volume_rpcapi @@ -89,6 +91,7 @@ class SchedulerManagerTestCase(test.TestCase): volume_rpcapi.client.version_cap) self.assertEqual(get_min_obj.return_value, volume_rpcapi.client.serializer._base.version_cap) + self.assertIsNone(volume_rpcapi.client.serializer._base.manifest) @mock.patch('cinder.scheduler.driver.Scheduler.' 'update_service_capabilities') diff --git a/cinder/tests/unit/test_rpc.py b/cinder/tests/unit/test_rpc.py index f9e997e5789..f13bce9de67 100644 --- a/cinder/tests/unit/test_rpc.py +++ b/cinder/tests/unit/test_rpc.py @@ -37,14 +37,14 @@ class RPCAPITestCase(test.TestCase): @mock.patch('cinder.objects.Service.get_minimum_rpc_version', return_value='1.2') @mock.patch('cinder.objects.Service.get_minimum_obj_version', - return_value='1.7') + return_value='1.4') @mock.patch('cinder.rpc.get_client') def test_init(self, get_client, get_min_obj, get_min_rpc): def fake_get_client(target, version_cap, serializer): self.assertEqual(FakeAPI.TOPIC, target.topic) self.assertEqual(FakeAPI.RPC_API_VERSION, target.version) self.assertEqual('1.2', version_cap) - self.assertEqual('1.7', serializer.version_cap) + self.assertEqual('1.4', serializer.version_cap) get_client.side_effect = fake_get_client FakeAPI() diff --git a/cinder/tests/unit/test_volume.py b/cinder/tests/unit/test_volume.py index 51914adca95..1f1434efe03 100644 --- a/cinder/tests/unit/test_volume.py +++ b/cinder/tests/unit/test_volume.py @@ -505,14 +505,15 @@ class VolumeTestCase(BaseVolumeTestCase): @mock.patch('cinder.objects.service.Service.get_minimum_rpc_version') @mock.patch('cinder.objects.service.Service.get_minimum_obj_version') @mock.patch('cinder.rpc.LAST_RPC_VERSIONS', {'cinder-scheduler': '1.3'}) - @mock.patch('cinder.rpc.LAST_OBJ_VERSIONS', {'cinder-scheduler': '1.5'}) + @mock.patch('cinder.rpc.LAST_OBJ_VERSIONS', {'cinder-scheduler': '1.4'}) def test_reset(self, get_min_obj, get_min_rpc): vol_mgr = vol_manager.VolumeManager() scheduler_rpcapi = vol_mgr.scheduler_rpcapi self.assertEqual('1.3', scheduler_rpcapi.client.version_cap) - self.assertEqual('1.5', + self.assertEqual('1.4', scheduler_rpcapi.client.serializer._base.version_cap) + get_min_obj.return_value = objects.base.OBJ_VERSIONS.get_current() vol_mgr.reset() scheduler_rpcapi = vol_mgr.scheduler_rpcapi @@ -520,6 +521,7 @@ class VolumeTestCase(BaseVolumeTestCase): scheduler_rpcapi.client.version_cap) self.assertEqual(get_min_obj.return_value, scheduler_rpcapi.client.serializer._base.version_cap) + self.assertIsNone(scheduler_rpcapi.client.serializer._base.manifest) @mock.patch.object(vol_manager.VolumeManager, 'update_service_capabilities')