Make service object UUID not nullable

Fix the UUID entry for the newly added service attribute and
udpate the unit tests appropriately.

This makes the UUID entry in the service object not nullable
and fixes up the unit tests to work properly.  Also introduces
a unit test specifically for the online migration api in the db.

Closes-Bug: #1727091

Change-Id: I17d3a873cfc8f056c2d31f6c8710489785998d3c
This commit is contained in:
John Griffith 2017-10-24 16:50:24 -06:00
parent c1bee6c204
commit 950e693697
18 changed files with 250 additions and 103 deletions

View File

@ -568,9 +568,6 @@ def service_get_by_uuid(context, service_uuid):
@require_admin_context
def service_create(context, values):
if not values.get('uuid'):
values['uuid'] = str(uuid.uuid4())
service_ref = models.Service()
service_ref.update(values)
if not CONF.enable_new_services:
@ -600,14 +597,11 @@ def service_update(context, service_id, values):
def service_uuids_online_data_migration(context, max_count):
from cinder.objects import service
total = 0
updated = 0
total = model_query(context, models.Service).filter_by(uuid=None).count()
db_services = model_query(context, models.Service).filter_by(
uuid=None).limit(max_count)
uuid=None).limit(max_count).all()
for db_service in db_services:
total += 1
# The conversion in the Service object code
# will generate a UUID and save it for us.
service_obj = service.Service._from_db_object(

View File

@ -136,6 +136,7 @@ OBJ_VERSIONS.add('1.25', {'Group': '1.2'})
OBJ_VERSIONS.add('1.26', {'Snapshot': '1.5'})
OBJ_VERSIONS.add('1.27', {'Backup': '1.5', 'BackupImport': '1.5'})
OBJ_VERSIONS.add('1.28', {'Service': '1.5'})
OBJ_VERSIONS.add('1.29', {'Service': '1.6'})
class CinderObjectRegistry(base.VersionedObjectRegistry):

View File

@ -39,7 +39,8 @@ class Service(base.CinderPersistentObject, base.CinderObject,
# Version 1.3: Add replication fields
# Version 1.4: Add cluster fields
# Version 1.5: Add UUID field
VERSION = '1.5'
# Version 1.6: Modify UUID field to be not nullable
VERSION = '1.6'
OPTIONAL_FIELDS = ('cluster',)
@ -66,7 +67,7 @@ class Service(base.CinderPersistentObject, base.CinderObject,
'frozen': fields.BooleanField(default=False),
'active_backend_id': fields.StringField(nullable=True),
'uuid': fields.StringField(nullable=True),
'uuid': fields.StringField(),
}
def obj_make_compatible(self, primitive, target_version):
@ -86,8 +87,10 @@ class Service(base.CinderPersistentObject, base.CinderObject,
def _from_db_object(context, service, db_service, expected_attrs=None):
expected_attrs = expected_attrs or []
for name, field in service.fields.items():
if name in Service.OPTIONAL_FIELDS:
if ((name == 'uuid' and not db_service.get(name)) or
name in service.OPTIONAL_FIELDS):
continue
value = db_service.get(name)
if isinstance(field, fields.IntegerField):
value = value or 0

View File

@ -770,7 +770,8 @@ class AdminActionsTest(BaseAdminTest):
mock_service_get_all):
mock_service_get_all.return_value = [
{'availability_zone': "az1", 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
# admin context
mock_check_support.return_value = True
# current status is dependent on argument: test_status.

View File

@ -488,7 +488,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'fake_az', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=5)
@ -524,7 +525,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'fake_az', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=1)
# Create a backup with metadata
@ -564,7 +566,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'fake_az', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=5, status='in-use')
@ -595,7 +598,8 @@ class BackupsAPITestCase(test.TestCase):
def test_create_backup_inuse_force(self, _mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'fake_az', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=5, status='in-use')
backup = utils.create_backup(self.context, volume.id,
@ -635,7 +639,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'fake_az', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=5, status='available')
@ -770,7 +775,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'fake_az', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=5)
snapshot = None
@ -819,7 +825,8 @@ class BackupsAPITestCase(test.TestCase):
self, _mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'fake_az', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=5)
@ -973,7 +980,8 @@ class BackupsAPITestCase(test.TestCase):
self, _mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'fake_az', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=5, status='available')
@ -1009,22 +1017,27 @@ class BackupsAPITestCase(test.TestCase):
empty_service = []
# service host not match with volume's host
host_not_match = [{'availability_zone': 'fake_az', 'host': alt_host,
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
# service az not match with volume's az
az_not_match = [{'availability_zone': 'strange_az', 'host': testhost,
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'}]
# service disabled
disabled_service = []
# dead service that last reported at 20th century
dead_service = [{'availability_zone': 'fake_az', 'host': alt_host,
'disabled': 0, 'updated_at': '1989-04-16 02:55:44'}]
'disabled': 0, 'updated_at': '1989-04-16 02:55:44',
'uuid': '6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'}]
# first service's host not match but second one works.
multi_services = [{'availability_zone': 'fake_az', 'host': alt_host,
'disabled': 0, 'updated_at': timeutils.utcnow()},
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': '18417850-2ca9-43d1-9619-ae16bfb0f655'},
{'availability_zone': 'fake_az', 'host': testhost,
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'f838f35c-4035-464f-9792-ce60e390c13d'}]
# Setup mock to run through the following service cases
_mock_service_get_all.side_effect = [empty_service,
@ -1075,11 +1088,14 @@ class BackupsAPITestCase(test.TestCase):
def test_get_available_backup_service(self, _mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost1',
'disabled': 0, 'updated_at': timeutils.utcnow()},
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'},
{'availability_zone': 'az2', 'host': 'testhost2',
'disabled': 0, 'updated_at': timeutils.utcnow()},
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'},
{'availability_zone': 'az2', 'host': 'testhost3',
'disabled': 0, 'updated_at': timeutils.utcnow()}, ]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': '6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'}, ]
actual_host = self.backup_api._get_available_backup_service_host(
None, 'az1')
self.assertEqual('testhost1', actual_host)
@ -1095,9 +1111,11 @@ class BackupsAPITestCase(test.TestCase):
self, _mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost1',
'disabled': 0, 'updated_at': timeutils.utcnow()},
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'},
{'availability_zone': 'az2', 'host': 'testhost2',
'disabled': 0, 'updated_at': timeutils.utcnow()}, ]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'}, ]
self.override_config('backup_use_same_host', True)
actual_host = self.backup_api._get_available_backup_service_host(
None, 'az1')
@ -1113,7 +1131,8 @@ class BackupsAPITestCase(test.TestCase):
def test_delete_backup_available(self, _mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
backup = utils.create_backup(self.context,
status=fields.BackupStatus.AVAILABLE,
availability_zone='az1', host='testhost')
@ -1136,7 +1155,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
backup = utils.create_backup(self.context,
status=fields.BackupStatus.AVAILABLE,
availability_zone='az1', host='testhost')
@ -1164,7 +1184,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
backup = utils.create_backup(self.context,
status=fields.BackupStatus.ERROR,
availability_zone='az1', host='testhost')
@ -1222,7 +1243,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, size=5)
backup = utils.create_backup(self.context, volume.id,
status=fields.BackupStatus.AVAILABLE)
@ -1253,7 +1275,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all):
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost',
'disabled': 0, 'updated_at': '1775-04-19 05:00:00'}]
'disabled': 0, 'updated_at': '1775-04-19 05:00:00',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
backup = utils.create_backup(self.context, status='available')
req = webob.Request.blank('/v2/%s/backups/%s' % (
fake.PROJECT_ID, backup.id))
@ -1351,7 +1374,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
_mock_volume_api_create.side_effect = fake_volume_api_create
backup = utils.create_backup(self.context, size=5,
@ -1386,7 +1410,8 @@ class BackupsAPITestCase(test.TestCase):
_mock_volume_api_create.side_effect = fake_volume_api_create
_mock_service_get_all.return_value = [
{'availability_zone': 'az1', 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow()}]
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
backup = utils.create_backup(self.context, size=5,
status=fields.BackupStatus.AVAILABLE,

View File

@ -70,7 +70,9 @@ class CapabilitiesAPITest(test.TestCase):
@mock.patch('cinder.volume.rpcapi.VolumeAPI.get_capabilities',
rpcapi_get_capabilities)
def test_capabilities_summary(self, mock_services):
mock_services.return_value = [{'name': 'fake', 'host': 'fake_host'}]
mock_services.return_value = [
{'name': 'fake', 'host': 'fake_host',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
req = fakes.HTTPRequest.blank('/fake/capabilities/fake')
req.environ['cinder.context'] = self.ctxt
res = self.controller.show(req, 'fake')
@ -113,7 +115,9 @@ class CapabilitiesAPITest(test.TestCase):
@mock.patch('cinder.volume.rpcapi.VolumeAPI.get_capabilities')
def test_get_capabilities_rpc_timeout(self, mock_rpc, mock_services):
mock_rpc.side_effect = oslo_messaging.MessagingTimeout
mock_services.return_value = [{'name': 'fake'}]
mock_services.return_value = [
{'name': 'fake',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
req = fakes.HTTPRequest.blank('/fake/capabilities/fake')
req.environ['cinder.context'] = self.ctxt

View File

@ -31,37 +31,46 @@ curr_time = datetime.datetime(2013, 7, 3, 0, 0, 1)
SERVICE_LIST = [
{'created_at': created_time, 'updated_at': curr_time,
'host': 'test.host.1', 'topic': 'cinder-volume', 'disabled': 0,
'availability_zone': 'cinder'},
'availability_zone': 'cinder',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'},
{'created_at': created_time, 'updated_at': curr_time,
'host': 'test.host.1', 'topic': 'cinder-volume', 'disabled': 0,
'availability_zone': 'cinder'},
'availability_zone': 'cinder',
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'},
{'created_at': created_time, 'updated_at': curr_time,
'host': 'test.host.1', 'topic': 'cinder-volume', 'disabled': 0,
'availability_zone': 'cinder'},
'availability_zone': 'cinder',
'uuid': '6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'},
{'created_at': created_time, 'updated_at': curr_time,
'host': 'test.host.1', 'topic': 'cinder-volume', 'disabled': 0,
'availability_zone': 'cinder'},
'availability_zone': 'cinder',
'uuid': '18417850-2ca9-43d1-9619-ae16bfb0f655'},
{'created_at': created_time, 'updated_at': None,
'host': 'test.host.1', 'topic': 'cinder-volume', 'disabled': 0,
'availability_zone': 'cinder'},
'availability_zone': 'cinder',
'uuid': 'f838f35c-4035-464f-9792-ce60e390c13d'},
]
LIST_RESPONSE = [{'service-status': 'available', 'service': 'cinder-volume',
'zone': 'cinder', 'service-state': 'enabled',
'host_name': 'test.host.1', 'last-update': curr_time},
'host_name': 'test.host.1', 'last-update': curr_time,
},
{'service-status': 'available', 'service': 'cinder-volume',
'zone': 'cinder', 'service-state': 'enabled',
'host_name': 'test.host.1', 'last-update': curr_time},
'host_name': 'test.host.1', 'last-update': curr_time,
},
{'service-status': 'available', 'service': 'cinder-volume',
'zone': 'cinder', 'service-state': 'enabled',
'host_name': 'test.host.1', 'last-update': curr_time},
'host_name': 'test.host.1', 'last-update': curr_time,
},
{'service-status': 'available', 'service': 'cinder-volume',
'zone': 'cinder', 'service-state': 'enabled',
'host_name': 'test.host.1', 'last-update': curr_time},
'host_name': 'test.host.1', 'last-update': curr_time,
},
{'service-status': 'unavailable', 'service': 'cinder-volume',
'zone': 'cinder', 'service-state': 'enabled',
'host_name': 'test.host.1', 'last-update': None},
]
'host_name': 'test.host.1', 'last-update': None,
}, ]
def stub_utcnow(with_timezone=False):

View File

@ -47,7 +47,8 @@ fake_services_list = [
'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2),
'created_at': datetime.datetime(2012, 9, 18, 2, 46, 27),
'disabled_reason': 'test1',
'modified_at': ''},
'modified_at': '',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'},
{'binary': 'cinder-volume',
'host': 'host1',
'cluster_name': None,
@ -57,7 +58,8 @@ fake_services_list = [
'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5),
'created_at': datetime.datetime(2012, 9, 18, 2, 46, 27),
'disabled_reason': 'test2',
'modified_at': ''},
'modified_at': '',
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'},
{'binary': 'cinder-scheduler',
'host': 'host2',
'cluster_name': 'cluster1',
@ -67,7 +69,8 @@ fake_services_list = [
'updated_at': datetime.datetime(2012, 9, 19, 6, 55, 34),
'created_at': datetime.datetime(2012, 9, 18, 2, 46, 28),
'disabled_reason': '',
'modified_at': ''},
'modified_at': '',
'uuid': '6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'},
{'binary': 'cinder-volume',
'host': 'host2',
'cluster_name': 'cluster1',
@ -77,7 +80,8 @@ fake_services_list = [
'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38),
'created_at': datetime.datetime(2012, 9, 18, 2, 46, 28),
'disabled_reason': 'test4',
'modified_at': ''},
'modified_at': '',
'uuid': '18417850-2ca9-43d1-9619-ae16bfb0f655'},
{'binary': 'cinder-volume',
'host': 'host2',
'cluster_name': 'cluster2',
@ -87,7 +91,8 @@ fake_services_list = [
'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38),
'created_at': datetime.datetime(2012, 9, 18, 2, 46, 28),
'disabled_reason': 'test5',
'modified_at': datetime.datetime(2012, 10, 29, 13, 42, 5)},
'modified_at': datetime.datetime(2012, 10, 29, 13, 42, 5),
'uuid': 'f838f35c-4035-464f-9792-ce60e390c13d'},
{'binary': 'cinder-volume',
'host': 'host2',
'cluster_name': 'cluster2',
@ -97,7 +102,8 @@ fake_services_list = [
'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38),
'created_at': datetime.datetime(2012, 9, 18, 2, 46, 28),
'disabled_reason': '',
'modified_at': datetime.datetime(2012, 9, 18, 8, 1, 38)},
'modified_at': datetime.datetime(2012, 9, 18, 8, 1, 38),
'uuid': 'f2825a00-cc2f-493d-9635-003e01db8b3d'},
{'binary': 'cinder-scheduler',
'host': 'host2',
'cluster_name': None,
@ -107,7 +113,8 @@ fake_services_list = [
'updated_at': None,
'created_at': datetime.datetime(2012, 9, 18, 2, 46, 28),
'disabled_reason': '',
'modified_at': None},
'modified_at': None,
'uuid': '35fcf841-1974-4944-a798-1fb6d0a44972'},
]

View File

@ -62,9 +62,11 @@ def service_get(context, service_id, backend_match_level=None, host=None,
check for existence of a host, so the content returned doesn't matter.
"""
if host == 'host_ok':
return {'disabled': False}
return {'disabled': False,
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}
if host == 'host_disabled':
return {'disabled': True}
return {'disabled': True,
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'}
raise exception.ServiceNotFound(service_id=host)
# Some of the tests check that volume types are correctly validated during a

View File

@ -223,11 +223,13 @@ def fake_snapshot_update(self, context, *args, **param):
def fake_service_get_all(*args, **kwargs):
return [{'availability_zone': "zone1:host1", "disabled": 0}]
return [{'availability_zone': "zone1:host1", "disabled": 0,
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
def fake_service_get_all_by_topic(context, topic, disabled=None):
return [{'availability_zone': "zone1:host1", "disabled": 0}]
return [{'availability_zone': "zone1:host1", "disabled": 0,
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'}]
def fake_snapshot_get(self, context, snapshot_id):

View File

@ -43,7 +43,7 @@ object_data = {
'QualityOfServiceSpecs': '1.0-0b212e0a86ee99092229874e03207fe8',
'QualityOfServiceSpecsList': '1.0-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'RequestSpec': '1.1-b0bd1a28d191d75648901fa853e8a733',
'Service': '1.5-dfa465098dd9017bad825cab55eacc93',
'Service': '1.6-e881b6b324151dd861e09cdfffcdaccd',
'ServiceList': '1.1-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'Snapshot': '1.5-ac1cdbd5b89588f6a8f44afdf6b8b201',
'SnapshotList': '1.0-15ecf022a68ddbb8c2a6739cfc9f8f5e',
@ -101,6 +101,12 @@ class TestObjectVersions(test.TestCase):
# db model and object match.
def _check_table_matched(db_model, cls):
for column in db_model.__table__.columns:
# NOTE(jdg): Model and Object don't match intentionally here
if (column.name in cls.fields and
(column.name == 'uuid' and name == 'Service')):
continue
# NOTE(xyang): Skip the comparison of the colume name
# group_type_id in table Group because group_type_id
# is in the object Group but it is stored in a different

View File

@ -47,7 +47,8 @@ class FakeHostManager(host_manager.HostManager):
'reserved_percentage': 10,
'volume_backend_name': 'lvm1',
'timestamp': UTC_NOW,
'multiattach': True},
'multiattach': True,
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'},
'host2': {'total_capacity_gb': 2048,
'free_capacity_gb': 300,
'allocated_capacity_gb': 1748,
@ -57,7 +58,8 @@ class FakeHostManager(host_manager.HostManager):
'thick_provisioning_support': False,
'reserved_percentage': 10,
'volume_backend_name': 'lvm2',
'timestamp': UTC_NOW},
'timestamp': UTC_NOW,
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'},
'host3': {'total_capacity_gb': 512,
'free_capacity_gb': 256,
'allocated_capacity_gb': 256,
@ -67,7 +69,8 @@ class FakeHostManager(host_manager.HostManager):
'thick_provisioning_support': True,
'reserved_percentage': 0,
'volume_backend_name': 'lvm3',
'timestamp': UTC_NOW},
'timestamp': UTC_NOW,
'uuid': '6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'},
'host4': {'total_capacity_gb': 2048,
'free_capacity_gb': 200,
'allocated_capacity_gb': 1848,
@ -78,7 +81,8 @@ class FakeHostManager(host_manager.HostManager):
'reserved_percentage': 5,
'volume_backend_name': 'lvm4',
'timestamp': UTC_NOW,
'consistent_group_snapshot_enabled': True},
'consistent_group_snapshot_enabled': True,
'uuid': '18417850-2ca9-43d1-9619-ae16bfb0f655'},
'host5': {'total_capacity_gb': 'infinite',
'free_capacity_gb': 'unknown',
'allocated_capacity_gb': 1548,
@ -87,7 +91,8 @@ class FakeHostManager(host_manager.HostManager):
'thin_provisioning_support': True,
'thick_provisioning_support': False,
'reserved_percentage': 5,
'timestamp': UTC_NOW},
'timestamp': UTC_NOW,
'uuid': 'f838f35c-4035-464f-9792-ce60e390c13d'},
}
@ -150,15 +155,20 @@ class FakeNovaClient(object):
def mock_host_manager_db_calls(mock_obj, disabled=None):
services = [
dict(id=1, host='host1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'),
dict(id=2, host='host2', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='4200b32b-0bf9-436c-86b2-0675f6ac218e'),
dict(id=3, host='host3', topic='volume', disabled=False,
availability_zone='zone2', updated_at=timeutils.utcnow()),
availability_zone='zone2', updated_at=timeutils.utcnow(),
uuid='4200b32b-0bf9-436c-86b2-0675f6ac218e'),
dict(id=4, host='host4', topic='volume', disabled=False,
availability_zone='zone3', updated_at=timeutils.utcnow()),
availability_zone='zone3', updated_at=timeutils.utcnow(),
uuid='18417850-2ca9-43d1-9619-ae16bfb0f655'),
dict(id=5, host='host5', topic='volume', disabled=False,
availability_zone='zone3', updated_at=timeutils.utcnow()),
availability_zone='zone3', updated_at=timeutils.utcnow(),
uuid='f838f35c-4035-464f-9792-ce60e390c13d'),
]
if disabled is None:
mock_obj.return_value = services

View File

@ -502,11 +502,14 @@ class HostManagerTestCase(test.TestCase):
_mock_service_is_up.return_value = True
services = [
dict(id=1, host='host1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'),
dict(id=2, host='host2', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='4200b32b-0bf9-436c-86b2-0675f6ac218e'),
dict(id=3, host='host3', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'),
]
_mock_service_get_all.return_value = services
# Create host_manager again to let db.service_get_all mock run
@ -553,7 +556,8 @@ class HostManagerTestCase(test.TestCase):
services = [
# This is the first call to utcnow()
dict(id=1, host='host1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7',)
]
mocked_service_states = {
@ -659,19 +663,23 @@ class HostManagerTestCase(test.TestCase):
dict(id=1, host='host1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow(),
binary=None, deleted=False, created_at=None, modified_at=None,
report_count=0, deleted_at=None, disabled_reason=None),
report_count=0, deleted_at=None, disabled_reason=None,
uuid='a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'),
dict(id=2, host='host2', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow(),
binary=None, deleted=False, created_at=None, modified_at=None,
report_count=0, deleted_at=None, disabled_reason=None),
report_count=0, deleted_at=None, disabled_reason=None,
uuid='4200b32b-0bf9-436c-86b2-0675f6ac218e'),
dict(id=3, host='host3', topic='volume', disabled=False,
availability_zone='zone2', updated_at=timeutils.utcnow(),
binary=None, deleted=False, created_at=None, modified_at=None,
report_count=0, deleted_at=None, disabled_reason=None),
report_count=0, deleted_at=None, disabled_reason=None,
uuid='6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'),
dict(id=4, host='host4', topic='volume', disabled=False,
availability_zone='zone3', updated_at=timeutils.utcnow(),
binary=None, deleted=False, created_at=None, modified_at=None,
report_count=0, deleted_at=None, disabled_reason=None),
report_count=0, deleted_at=None, disabled_reason=None,
uuid='18417850-2ca9-43d1-9619-ae16bfb0f655'),
]
service_objs = []
@ -759,11 +767,14 @@ class HostManagerTestCase(test.TestCase):
services = [
dict(id=1, host='host1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'),
dict(id=2, host='host2@back1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='4200b32b-0bf9-436c-86b2-0675f6ac218e'),
dict(id=3, host='host2@back2', topic='volume', disabled=False,
availability_zone='zone2', updated_at=timeutils.utcnow()),
availability_zone='zone2', updated_at=timeutils.utcnow(),
uuid='6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'),
]
mocked_service_states = {
@ -982,9 +993,11 @@ class HostManagerTestCase(test.TestCase):
services = [
dict(id=1, host='host1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'),
dict(id=2, host='host2@back1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow())
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='4200b32b-0bf9-436c-86b2-0675f6ac218e')
]
mocked_service_states = {
@ -1074,9 +1087,11 @@ class HostManagerTestCase(test.TestCase):
services = [
dict(id=1, host='host1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow()),
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'),
dict(id=2, host='host2@back1', topic='volume', disabled=False,
availability_zone='zone1', updated_at=timeutils.utcnow())
availability_zone='zone1', updated_at=timeutils.utcnow(),
uuid='4200b32b-0bf9-436c-86b2-0675f6ac218e')
]
mocked_service_states = {

View File

@ -335,8 +335,10 @@ class TestCinderManageCmd(test.TestCase):
@mock.patch('cinder.context.get_admin_context')
def test_host_commands_list(self, get_admin_context, service_get_all):
get_admin_context.return_value = mock.sentinel.ctxt
service_get_all.return_value = [{'host': 'fake-host',
'availability_zone': 'fake-az'}]
service_get_all.return_value = [
{'host': 'fake-host',
'availability_zone': 'fake-az',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
with mock.patch('sys.stdout', new=six.StringIO()) as fake_out:
expected_out = ("%(host)-25s\t%(zone)-15s\n" %
@ -356,10 +358,13 @@ class TestCinderManageCmd(test.TestCase):
def test_host_commands_list_with_zone(self, get_admin_context,
service_get_all):
get_admin_context.return_value = mock.sentinel.ctxt
service_get_all.return_value = [{'host': 'fake-host',
'availability_zone': 'fake-az1'},
{'host': 'fake-host',
'availability_zone': 'fake-az2'}]
service_get_all.return_value = [
{'host': 'fake-host',
'availability_zone': 'fake-az1',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'},
{'host': 'fake-host',
'availability_zone': 'fake-az2',
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'}]
with mock.patch('sys.stdout', new=six.StringIO()) as fake_out:
expected_out = ("%(host)-25s\t%(zone)-15s\n" %
@ -692,7 +697,8 @@ class TestCinderManageCmd(test.TestCase):
'disabled': False,
'rpc_current_version': '1.1',
'object_current_version': '1.1',
'cluster_name': 'my_cluster'}
'cluster_name': 'my_cluster',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}
for binary in ('volume', 'scheduler', 'backup'):
service['binary'] = 'cinder-%s' % binary
self._test_service_commands_list(service)
@ -704,7 +710,8 @@ class TestCinderManageCmd(test.TestCase):
'updated_at': None,
'disabled': False,
'rpc_current_version': '1.1',
'object_current_version': '1.1'}
'object_current_version': '1.1',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}
for binary in ('volume', 'scheduler', 'backup'):
service['binary'] = 'cinder-%s' % binary
self._test_service_commands_list(service)
@ -965,7 +972,10 @@ class TestCinderManageCmd(test.TestCase):
self.assertEqual(2, exit)
@mock.patch('cinder.db.service_destroy')
@mock.patch('cinder.db.service_get', return_value = {'id': '12'})
@mock.patch(
'cinder.db.service_get',
return_value = {'id': '12',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'})
def test_remove_service_success(self, mock_get_by_args,
mock_service_destroy):
service_commands = cinder_manage.ServiceCommands()

View File

@ -162,6 +162,54 @@ class DBAPIServiceTestCase(BaseTest):
"""Unit tests for cinder.db.api.service_*."""
def test_service_uuid_migrations(self):
# Force create one entry with no UUID
sqlalchemy_api.service_create(self.ctxt, {
'host': 'host1',
'binary': 'cinder-volume',
'topic': 'volume', })
# Create another one with a valid UUID
sqlalchemy_api.service_create(self.ctxt, {
'host': 'host2',
'binary': 'cinder-volume',
'topic': 'volume',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'})
# Run the migration and verify that we updated 1 entry
total, updated = db.service_uuids_online_data_migration(
self.ctxt, 10)
self.assertEqual(1, total)
self.assertEqual(1, updated)
def test_service_uuid_migrations_with_limit(self):
sqlalchemy_api.service_create(self.ctxt, {
'host': 'host1',
'binary': 'cinder-volume',
'topic': 'volume', })
sqlalchemy_api.service_create(self.ctxt, {
'host': 'host2',
'binary': 'cinder-volume',
'topic': 'volume', })
sqlalchemy_api.service_create(self.ctxt, {
'host': 'host3',
'binary': 'cinder-volume',
'topic': 'volume', })
# Run the migration and verify that we updated 1 entry
total, updated = db.service_uuids_online_data_migration(
self.ctxt, 2)
self.assertEqual(3, total)
self.assertEqual(2, updated)
# Now get the rest, intentionally setting max > what we should have
total, updated = db.service_uuids_online_data_migration(
self.ctxt, 2)
self.assertEqual(1, total)
self.assertEqual(1, updated)
def test_service_create(self):
# Add a cluster value to the service
values = {'cluster_name': 'cluster'}

View File

@ -147,7 +147,8 @@ class ServiceTestCase(test.TestCase):
'topic': self.topic,
'report_count': 0,
'availability_zone': 'nova',
'id': 1}
'id': 1,
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}
self.ctxt = context.get_admin_context()
def _check_app(self, app, cluster=None, cluster_exists=None,

View File

@ -29,7 +29,8 @@ class AvailabilityZoneTestCase(base.BaseVolumeTestCase):
super(AvailabilityZoneTestCase, self).setUp()
self.get_all = self.patch(
'cinder.db.service_get_all', autospec=True,
return_value = [{'availability_zone': 'a', 'disabled': False}])
return_value=[{'availability_zone': 'a', 'disabled': False,
'uuid': 'f838f35c-4035-464f-9792-ce60e390c13d'}])
def test_list_availability_zones_cached(self):
azs = self.volume_api.list_availability_zones(enable_cache=True)
@ -80,10 +81,12 @@ class AvailabilityZoneTestCase(base.BaseVolumeTestCase):
{
'availability_zone': 'a',
'disabled': False,
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824',
},
{
'availability_zone': 'b',
'disabled': False,
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e',
},
]
azs = self.volume_api.list_availability_zones(enable_cache=True)
@ -99,10 +102,14 @@ class AvailabilityZoneTestCase(base.BaseVolumeTestCase):
return obj['name']
self.get_all.return_value = [
{'availability_zone': 'ping', 'disabled': 0},
{'availability_zone': 'ping', 'disabled': 1},
{'availability_zone': 'pong', 'disabled': 0},
{'availability_zone': 'pung', 'disabled': 1},
{'availability_zone': 'ping', 'disabled': 0,
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'},
{'availability_zone': 'ping', 'disabled': 1,
'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'},
{'availability_zone': 'pong', 'disabled': 0,
'uuid': '6d91e7f5-ca17-4e3b-bf4f-19ca77166dd7'},
{'availability_zone': 'pung', 'disabled': 1,
'uuid': '18417850-2ca9-43d1-9619-ae16bfb0f655'},
]
volume_api = cinder.volume.api.API()

View File

@ -1083,7 +1083,9 @@ class VolumeTestCase(base.BaseVolumeTestCase):
with mock.patch('cinder.db.service_get_all') as mock_get_service, \
mock.patch.object(volume_api,
'list_availability_zones') as mock_get_azs:
mock_get_service.return_value = [{'host': 'foo'}]
mock_get_service.return_value = [
{'host': 'foo',
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
mock_get_azs.return_value = {}
volume_api.create(self.context,
size=1,