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'),
import mock
+from oslo_config import cfg
+
from cinder import context
from cinder import exception
from cinder.openstack.common import imageutils
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):
'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):
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.