Merge "Create volume from snapshot must be in the same AZ as snapshot"

This commit is contained in:
Jenkins 2013-07-27 15:59:02 +00:00 committed by Gerrit Code Review
commit 9f0bb808b8
2 changed files with 100 additions and 7 deletions

View File

@ -89,7 +89,8 @@ class VolumeTestCase(test.TestCase):
@staticmethod
def _create_volume(size=0, snapshot_id=None, image_id=None,
source_volid=None, metadata=None, status="creating"):
source_volid=None, metadata=None, status="creating",
availability_zone=None):
"""Create a volume object."""
vol = {}
vol['size'] = size
@ -98,7 +99,8 @@ class VolumeTestCase(test.TestCase):
vol['source_volid'] = source_volid
vol['user_id'] = 'fake'
vol['project_id'] = 'fake'
vol['availability_zone'] = CONF.storage_availability_zone
vol['availability_zone'] = \
availability_zone or CONF.storage_availability_zone
vol['status'] = status
vol['attach_status'] = "detached"
vol['host'] = CONF.host
@ -353,6 +355,41 @@ class VolumeTestCase(test.TestCase):
description='fake_desc',
snapshot=snapshot)
def test_create_volume_from_snapshot_fail_wrong_az(self):
"""Test volume can't be created from snapshot in a different az."""
volume_api = cinder.volume.api.API()
def fake_list_availability_zones():
return ({'name': 'nova', 'available': True},
{'name': 'az2', 'available': True})
self.stubs.Set(volume_api,
'list_availability_zones',
fake_list_availability_zones)
volume_src = self._create_volume(availability_zone='az2')
self.volume.create_volume(self.context, volume_src['id'])
snapshot = self._create_snapshot(volume_src['id'])
self.volume.create_snapshot(self.context, volume_src['id'],
snapshot['id'])
snapshot = db.snapshot_get(self.context, snapshot['id'])
volume_dst = volume_api.create(self.context,
size=1,
name='fake_name',
description='fake_desc',
snapshot=snapshot)
self.assertEqual(volume_dst['availability_zone'], 'az2')
self.assertRaises(exception.InvalidInput,
volume_api.create,
self.context,
size=1,
name='fake_name',
description='fake_desc',
snapshot=snapshot,
availability_zone='nova')
def test_create_volume_with_invalid_exclusive_options(self):
"""Test volume create with multiple exclusive options fails."""
volume_api = cinder.volume.api.API()
@ -1394,6 +1431,39 @@ class VolumeTestCase(test.TestCase):
self.volume.delete_volume(self.context, volume_dst['id'])
self.volume.delete_volume(self.context, volume_src['id'])
def test_create_volume_from_sourcevol_fail_wrong_az(self):
"""Test volume can't be cloned from an other volume in different az."""
volume_api = cinder.volume.api.API()
def fake_list_availability_zones():
return ({'name': 'nova', 'available': True},
{'name': 'az2', 'available': True})
self.stubs.Set(volume_api,
'list_availability_zones',
fake_list_availability_zones)
volume_src = self._create_volume(availability_zone='az2')
self.volume.create_volume(self.context, volume_src['id'])
volume_src = db.volume_get(self.context, volume_src['id'])
volume_dst = volume_api.create(self.context,
size=1,
name='fake_name',
description='fake_desc',
source_volume=volume_src)
self.assertEqual(volume_dst['availability_zone'], 'az2')
self.assertRaises(exception.InvalidInput,
volume_api.create,
self.context,
size=1,
name='fake_name',
description='fake_desc',
source_volume=volume_src,
availability_zone='nova')
def test_create_volume_from_sourcevol_with_glance_metadata(self):
"""Test glance metadata can be correctly copied to new volume."""
def fake_create_cloned_volume(volume, src_vref):

View File

@ -45,9 +45,14 @@ volume_host_opt = cfg.BoolOpt('snapshot_same_host',
default=True,
help='Create volume from snapshot at the host '
'where snapshot resides')
volume_same_az_opt = cfg.BoolOpt('cloned_volume_same_az',
default=True,
help='Ensure that the new volumes are the '
'same AZ as snapshot or source volume')
CONF = cfg.CONF
CONF.register_opt(volume_host_opt)
CONF.register_opt(volume_same_az_opt)
CONF.import_opt('storage_availability_zone', 'cinder.volume.manager')
LOG = logging.getLogger(__name__)
@ -160,6 +165,29 @@ class API(base.Base):
msg = _('Image minDisk size is larger than the volume size.')
raise exception.InvalidInput(reason=msg)
if availability_zone is None:
if snapshot is not None:
availability_zone = snapshot['volume']['availability_zone']
elif source_volume is not None:
availability_zone = source_volume['availability_zone']
else:
availability_zone = CONF.storage_availability_zone
else:
self._check_availabilty_zone(availability_zone)
if CONF.cloned_volume_same_az:
if (snapshot and
snapshot['volume']['availability_zone'] !=
availability_zone):
msg = _("Volume must be in the same "
"availability zone as the snapshot")
raise exception.InvalidInput(reason=msg)
elif source_volume and \
source_volume['availability_zone'] != availability_zone:
msg = _("Volume must be in the same "
"availability zone as the source volume")
raise exception.InvalidInput(reason=msg)
if not volume_type and not source_volume:
volume_type = volume_types.get_default_volume_type()
@ -198,11 +226,6 @@ class API(base.Base):
'd_consumed': _consumed(over)})
raise exception.VolumeLimitExceeded(allowed=quotas[over])
if availability_zone is None:
availability_zone = CONF.storage_availability_zone
else:
self._check_availabilty_zone(availability_zone)
self._check_metadata_properties(context, metadata)
options = {'size': size,
'user_id': context.user_id,