From: Edmund Rhudy Date: Thu, 27 Aug 2015 17:49:58 +0000 (-0400) Subject: Adds allow_availability_zone_fallback option to Cinder X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=b85d2812a8256ff82934d150dbc4909e041d8b31;p=openstack-build%2Fcinder-build.git 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 --- diff --git a/cinder/common/config.py b/cinder/common/config.py index 594fefb07..f7314f70d 100644 --- a/cinder/common/config.py +++ b/cinder/common/config.py @@ -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'), diff --git a/cinder/tests/unit/volume/flows/test_create_volume_flow.py b/cinder/tests/unit/volume/flows/test_create_volume_flow.py index ddb92f98e..4cd1605a1 100644 --- a/cinder/tests/unit/volume/flows/test_create_volume_flow.py +++ b/cinder/tests/unit/volume/flows/test_create_volume_flow.py @@ -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): diff --git a/cinder/volume/flows/api/create_volume.py b/cinder/volume/flows/api/create_volume.py index 795043d8e..f1ab99d9d 100644 --- a/cinder/volume/flows/api/create_volume.py +++ b/cinder/volume/flows/api/create_volume.py @@ -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.