]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Adds allow_availability_zone_fallback option to Cinder
authorEdmund Rhudy <erhudy@bloomberg.net>
Thu, 27 Aug 2015 17:49:58 +0000 (13:49 -0400)
committerEdmund Rhudy <erhudy@bloomberg.net>
Thu, 3 Sep 2015 15:13:02 +0000 (11:13 -0400)
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

cinder/common/config.py
cinder/tests/unit/volume/flows/test_create_volume_flow.py
cinder/volume/flows/api/create_volume.py

index 594fefb078c341632d9d64ff2104e485f0c093d3..f7314f70d97443e5927a361c54120beae4e4b7f5 100644 (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'),
index ddb92f98e47e915d8a066c32b7ae8028f605f13a..4cd1605a19d7e379854e08a6742d74db75ef7028 100644 (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):
 
index 795043d8e663e1065eb890b6047b1eebf1e28923..f1ab99d9de7f9d3d85676d81378c7228f3edc7fe 100644 (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.