Merge "Check type match on create from source/snap"
This commit is contained in:
@@ -956,10 +956,14 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
"""Test volume create from snapshot with types including mistmatch."""
|
||||
volume_api = cinder.volume.api.API()
|
||||
|
||||
db.volume_type_create(context.get_admin_context(),
|
||||
{'name': 'foo', 'extra_specs': {}})
|
||||
db.volume_type_create(context.get_admin_context(),
|
||||
{'name': 'biz', 'extra_specs': {}})
|
||||
db.volume_type_create(
|
||||
context.get_admin_context(),
|
||||
{'name': 'foo',
|
||||
'extra_specs': {'volume_backend_name': 'dev_1'}})
|
||||
|
||||
db.volume_type_create(
|
||||
context.get_admin_context(),
|
||||
{'name': 'biz', 'extra_specs': {'volume_backend_name': 'dev_2'}})
|
||||
|
||||
foo_type = db.volume_type_get_by_name(context.get_admin_context(),
|
||||
'foo')
|
||||
@@ -973,6 +977,9 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
|
||||
# Make sure the case of specifying a type that
|
||||
# doesn't match the snapshots type fails
|
||||
with mock.patch.object(cinder.volume.volume_types,
|
||||
'get_volume_type') as mock_get_type:
|
||||
mock_get_type.return_value = biz_type
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
volume_api.create,
|
||||
self.context,
|
||||
@@ -1009,10 +1016,14 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
"""Test volume create from source with types including mistmatch."""
|
||||
volume_api = cinder.volume.api.API()
|
||||
|
||||
db.volume_type_create(context.get_admin_context(),
|
||||
{'name': 'foo', 'extra_specs': {}})
|
||||
db.volume_type_create(context.get_admin_context(),
|
||||
{'name': 'biz', 'extra_specs': {}})
|
||||
db.volume_type_create(
|
||||
context.get_admin_context(),
|
||||
{'name': 'foo',
|
||||
'extra_specs': {'volume_backend_name': 'dev_1'}})
|
||||
|
||||
db.volume_type_create(
|
||||
context.get_admin_context(),
|
||||
{'name': 'biz', 'extra_specs': {'volume_backend_name': 'dev_2'}})
|
||||
|
||||
foo_type = db.volume_type_get_by_name(context.get_admin_context(),
|
||||
'foo')
|
||||
@@ -1025,8 +1036,9 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
'volume_type': biz_type,
|
||||
'volume_type_id': biz_type['id']}
|
||||
|
||||
# Make sure the case of specifying a type that
|
||||
# doesn't match the snapshots type fails
|
||||
with mock.patch.object(cinder.volume.volume_types,
|
||||
'get_volume_type') as mock_get_type:
|
||||
mock_get_type.return_value = biz_type
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
volume_api.create,
|
||||
self.context,
|
||||
@@ -1060,6 +1072,111 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
db.volume_type_destroy(context.get_admin_context(),
|
||||
biz_type['id'])
|
||||
|
||||
@mock.patch('cinder.volume.flows.api.create_volume.get_flow')
|
||||
def test_create_volume_from_source_with_same_backend(self, _get_flow):
|
||||
"""Test volume create from source with type mismatch same backend."""
|
||||
volume_api = cinder.volume.api.API()
|
||||
|
||||
foo_type = {
|
||||
'name': 'foo',
|
||||
'qos_specs_id': None,
|
||||
'deleted': False,
|
||||
'created_at': datetime.datetime(2015, 5, 8, 0, 40, 5, 408232),
|
||||
'updated_at': None,
|
||||
'extra_specs': {'volume_backend_name': 'dev_1'},
|
||||
'is_public': True,
|
||||
'deleted_at': None,
|
||||
'id': '29e43b50-2cd7-4d0c-8ddd-2119daab3a38',
|
||||
'description': None}
|
||||
|
||||
biz_type = {
|
||||
'name': 'biz',
|
||||
'qos_specs_id': None,
|
||||
'deleted': False,
|
||||
'created_at': datetime.datetime(2015, 5, 8, 0, 20, 5, 408232),
|
||||
'updated_at': None,
|
||||
'extra_specs': {'volume_backend_name': 'dev_1'},
|
||||
'is_public': True,
|
||||
'deleted_at': None,
|
||||
'id': '34e54c31-3bc8-5c1d-9fff-2225bcce4b59',
|
||||
'description': None}
|
||||
|
||||
source_vol = {'id': 1234,
|
||||
'status': 'available',
|
||||
'volume_size': 10,
|
||||
'volume_type': biz_type,
|
||||
'volume_type_id': biz_type['id']}
|
||||
|
||||
with mock.patch.object(cinder.volume.volume_types,
|
||||
'get_volume_type') as mock_get_type:
|
||||
mock_get_type.return_value = biz_type
|
||||
volume_api.create(self.context,
|
||||
size=1,
|
||||
name='fake_name',
|
||||
description='fake_desc',
|
||||
volume_type=foo_type,
|
||||
source_volume=source_vol)
|
||||
|
||||
@mock.patch('cinder.volume.flows.api.create_volume.get_flow')
|
||||
def test_create_from_source_and_snap_only_one_backend(self, _get_flow):
|
||||
"""Test create from source and snap with type mismatch one backend."""
|
||||
volume_api = cinder.volume.api.API()
|
||||
|
||||
foo_type = {
|
||||
'name': 'foo',
|
||||
'qos_specs_id': None,
|
||||
'deleted': False,
|
||||
'created_at': datetime.datetime(2015, 5, 8, 0, 40, 5, 408232),
|
||||
'updated_at': None,
|
||||
'extra_specs': {'some_key': 3},
|
||||
'is_public': True,
|
||||
'deleted_at': None,
|
||||
'id': '29e43b50-2cd7-4d0c-8ddd-2119daab3a38',
|
||||
'description': None}
|
||||
|
||||
biz_type = {
|
||||
'name': 'biz',
|
||||
'qos_specs_id': None,
|
||||
'deleted': False,
|
||||
'created_at': datetime.datetime(2015, 5, 8, 0, 20, 5, 408232),
|
||||
'updated_at': None,
|
||||
'extra_specs': {'some_other_key': 4},
|
||||
'is_public': True,
|
||||
'deleted_at': None,
|
||||
'id': '34e54c31-3bc8-5c1d-9fff-2225bcce4b59',
|
||||
'description': None}
|
||||
|
||||
source_vol = {'id': 1234,
|
||||
'status': 'available',
|
||||
'volume_size': 10,
|
||||
'volume_type': biz_type,
|
||||
'volume_type_id': biz_type['id']}
|
||||
|
||||
snapshot = {'id': 1234,
|
||||
'status': 'available',
|
||||
'volume_size': 10,
|
||||
'volume_type_id': biz_type['id']}
|
||||
|
||||
with mock.patch.object(db,
|
||||
'service_get_all_by_topic') as mock_get_service, \
|
||||
mock.patch.object(volume_api,
|
||||
'list_availability_zones') as mock_get_azs:
|
||||
mock_get_service.return_value = ['foo']
|
||||
mock_get_azs.return_value = {}
|
||||
volume_api.create(self.context,
|
||||
size=1,
|
||||
name='fake_name',
|
||||
description='fake_desc',
|
||||
volume_type=foo_type,
|
||||
source_volume=source_vol)
|
||||
|
||||
volume_api.create(self.context,
|
||||
size=1,
|
||||
name='fake_name',
|
||||
description='fake_desc',
|
||||
volume_type=foo_type,
|
||||
snapshot=snapshot)
|
||||
|
||||
def test_create_snapshot_driver_not_initialized(self):
|
||||
volume_src = tests_utils.create_volume(self.context,
|
||||
**self.volume_params)
|
||||
|
@@ -165,6 +165,26 @@ class API(base.Base):
|
||||
LOG.info(_LI("Availability Zones retrieved successfully."))
|
||||
return tuple(azs)
|
||||
|
||||
def _retype_is_possible(self, context,
|
||||
first_type_id, second_type_id,
|
||||
first_type=None, second_type=None):
|
||||
safe = False
|
||||
if len(self.db.service_get_all_by_topic(context,
|
||||
'cinder-volume',
|
||||
disabled=True)) == 1:
|
||||
safe = True
|
||||
else:
|
||||
type_a = first_type or volume_types.get_volume_type(
|
||||
context,
|
||||
first_type_id)
|
||||
type_b = second_type or volume_types.get_volume_type(
|
||||
context,
|
||||
second_type_id)
|
||||
if(volume_utils.matching_backend_name(type_a['extra_specs'],
|
||||
type_b['extra_specs'])):
|
||||
safe = True
|
||||
return safe
|
||||
|
||||
def create(self, context, size, name, description, snapshot=None,
|
||||
image_id=None, volume_type=None, metadata=None,
|
||||
availability_zone=None, source_volume=None,
|
||||
@@ -202,9 +222,14 @@ class API(base.Base):
|
||||
|
||||
if source_volume and volume_type:
|
||||
if volume_type['id'] != source_volume['volume_type_id']:
|
||||
if not self._retype_is_possible(
|
||||
context,
|
||||
volume_type['id'],
|
||||
source_volume['volume_type_id'],
|
||||
volume_type):
|
||||
msg = _("Invalid volume_type provided: %s (requested type "
|
||||
"must match source volume, "
|
||||
"or be omitted).") % volume_type
|
||||
"is not compatible; either match source volume, "
|
||||
"or omit type argument).") % volume_type['id']
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
# When cloning replica (for testing), volume type must be omitted
|
||||
@@ -215,9 +240,13 @@ class API(base.Base):
|
||||
|
||||
if snapshot and volume_type:
|
||||
if volume_type['id'] != snapshot['volume_type_id']:
|
||||
if not self._retype_is_possible(context,
|
||||
volume_type['id'],
|
||||
snapshot['volume_type_id'],
|
||||
volume_type):
|
||||
msg = _("Invalid volume_type provided: %s (requested "
|
||||
"type must match source snapshot, or be "
|
||||
"omitted).") % volume_type
|
||||
"type is not compatible; recommend omitting "
|
||||
"the type argument).") % volume_type['id']
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
# Determine the valid availability zones that the volume could be
|
||||
|
@@ -494,3 +494,12 @@ def append_host(host, pool):
|
||||
|
||||
new_host = "#".join([host, pool])
|
||||
return new_host
|
||||
|
||||
|
||||
def matching_backend_name(src_volume_type, volume_type):
|
||||
if src_volume_type.get('volume_backend_name') and \
|
||||
volume_type.get('volume_backend_name'):
|
||||
return src_volume_type.get('volume_backend_name') == \
|
||||
volume_type.get('volume_backend_name')
|
||||
else:
|
||||
return False
|
||||
|
Reference in New Issue
Block a user