Adds allow_availability_zone_fallback option to Cinder

This change adds allow_availability_zone_fallback, which allows Cinder
to fall back to an already configured availability zone if the
requested availability zone is unavailable.

Change-Id: Id6b43fc3243b4de82241cfab34ff10953b537874
DocImpact: cinder
Closes-Bug: #1489575
This commit is contained in:
Edmund Rhudy 2015-08-27 13:49:58 -04:00
parent 6dba657a29
commit b85d2812a8
3 changed files with 121 additions and 3 deletions

View File

@ -140,6 +140,12 @@ global_opts = [
help='Default availability zone for new volumes. If not set, '
'the storage_availability_zone option value is used as '
'the default for new volumes.'),
cfg.BoolOpt('allow_availability_zone_fallback',
default=False,
help='If the requested Cinder availability zone is '
'unavailable, fall back to the value of '
'default_availability_zone, then '
'storage_availability_zone, instead of failing.'),
cfg.StrOpt('default_volume_type',
default=None,
help='Default volume type to use'),

View File

@ -16,6 +16,8 @@
import mock
from oslo_config import cfg
from cinder import context
from cinder import exception
from cinder.openstack.common import imageutils
@ -30,6 +32,8 @@ from cinder.tests.unit.volume.flows import fake_volume_api
from cinder.volume.flows.api import create_volume
from cinder.volume.flows.manager import create_volume as create_volume_manager
CONF = cfg.CONF
class CreateVolumeFlowTestCase(test.TestCase):
@ -178,6 +182,103 @@ class CreateVolumeFlowTestCase(test.TestCase):
'cgsnapshot_id': None, }
self.assertEqual(expected_result, result)
@mock.patch('cinder.volume.volume_types.is_encrypted')
@mock.patch('cinder.volume.volume_types.get_volume_type_qos_specs')
@mock.patch('cinder.volume.flows.api.create_volume.'
'ExtractVolumeRequestTask.'
'_get_volume_type_id')
def test_extract_availability_zone_without_fallback(
self,
fake_get_type_id,
fake_get_qos,
fake_is_encrypted):
fake_image_service = fake_image.FakeImageService()
image_id = 3
image_meta = {}
image_meta['id'] = image_id
image_meta['status'] = 'active'
image_meta['size'] = 1
fake_image_service.create(self.ctxt, image_meta)
fake_key_manager = mock_key_mgr.MockKeyManager()
volume_type = 'type1'
task = create_volume.ExtractVolumeRequestTask(
fake_image_service,
{'nova'})
fake_is_encrypted.return_value = False
fake_get_type_id.return_value = 1
fake_get_qos.return_value = {'qos_specs': None}
self.assertRaises(exception.InvalidInput,
task.execute,
self.ctxt,
size=1,
snapshot=None,
image_id=image_id,
source_volume=None,
availability_zone='notnova',
volume_type=volume_type,
metadata=None,
key_manager=fake_key_manager,
source_replica=None,
consistencygroup=None,
cgsnapshot=None)
@mock.patch('cinder.volume.volume_types.is_encrypted')
@mock.patch('cinder.volume.volume_types.get_volume_type_qos_specs')
@mock.patch('cinder.volume.flows.api.create_volume.'
'ExtractVolumeRequestTask.'
'_get_volume_type_id')
def test_extract_availability_zone_with_fallback(
self,
fake_get_type_id,
fake_get_qos,
fake_is_encrypted):
self.override_config('allow_availability_zone_fallback', True)
fake_image_service = fake_image.FakeImageService()
image_id = 4
image_meta = {}
image_meta['id'] = image_id
image_meta['status'] = 'active'
image_meta['size'] = 1
fake_image_service.create(self.ctxt, image_meta)
fake_key_manager = mock_key_mgr.MockKeyManager()
volume_type = 'type1'
task = create_volume.ExtractVolumeRequestTask(
fake_image_service,
{'nova'})
fake_is_encrypted.return_value = False
fake_get_type_id.return_value = 1
fake_get_qos.return_value = {'qos_specs': None}
result = task.execute(self.ctxt,
size=1,
snapshot=None,
image_id=image_id,
source_volume=None,
availability_zone='does_not_exist',
volume_type=volume_type,
metadata=None,
key_manager=fake_key_manager,
source_replica=None,
consistencygroup=None,
cgsnapshot=None)
expected_result = {'size': 1,
'snapshot_id': None,
'source_volid': None,
'availability_zone': 'nova',
'volume_type': volume_type,
'volume_type_id': 1,
'encryption_key_id': None,
'qos_specs': None,
'source_replicaid': None,
'consistencygroup_id': None,
'cgsnapshot_id': None, }
self.assertEqual(expected_result, result)
class CreateVolumeFlowManagerTestCase(test.TestCase):

View File

@ -277,9 +277,20 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
availability_zone = CONF.storage_availability_zone
if availability_zone not in self.availability_zones:
msg = _("Availability zone '%s' is invalid") % (availability_zone)
LOG.warning(msg)
raise exception.InvalidInput(reason=msg)
if CONF.allow_availability_zone_fallback:
original_az = availability_zone
availability_zone = (
CONF.default_availability_zone or
CONF.storage_availability_zone)
LOG.warning(_LW("Availability zone '%(s_az)s' "
"not found, falling back to "
"'%(s_fallback_az)s'."),
{'s_az': original_az,
's_fallback_az': availability_zone})
else:
msg = _("Availability zone '%(s_az)s' is invalid.")
msg = msg % {'s_az': availability_zone}
raise exception.InvalidInput(reason=msg)
# If the configuration only allows cloning to the same availability
# zone then we need to enforce that.