Refresh az cache if target az is not found in cache
When creating volume, Cinder use az cache to validate az. This will lead a problem: If a new cinder-volume node with a different az is added to the OpenStack environment or admin change an exist cinder-volume's az, Creating volume with this az will fail until the cache is refreshed. This patch will refresh the cached az if the previous creating task failed to find the target. Change-Id: I3e884af1499ea2ddea1a3603d3d09c31a1f62811 Cloese-bug: #1693084
This commit is contained in:
parent
40f9e53a6b
commit
a85ae95a99
|
@ -210,6 +210,10 @@ class InvalidInput(Invalid):
|
|||
message = _("Invalid input received: %(reason)s")
|
||||
|
||||
|
||||
class InvalidAvailabilityZone(Invalid):
|
||||
message = _("Availability zone '%(az)s' is invalid.")
|
||||
|
||||
|
||||
class InvalidVolumeType(Invalid):
|
||||
message = _("Invalid volume type: %(reason)s")
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ class VolumeApiTest(test.TestCase):
|
|||
"availability_zone": "zonen:hostn"}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
self.assertRaises(exc.InvalidInput,
|
||||
self.assertRaises(exc.InvalidAvailabilityZone,
|
||||
self.controller.create,
|
||||
req, body)
|
||||
|
||||
|
|
|
@ -421,7 +421,7 @@ class VolumeApiTest(test.TestCase):
|
|||
vol = self._vol_in_request_body(availability_zone="zonen:hostn")
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.assertRaises(exception.InvalidAvailabilityZone,
|
||||
self.controller.create,
|
||||
req, body)
|
||||
|
||||
|
|
|
@ -243,6 +243,7 @@ class CreateVolumeFlowTestCase(test.TestCase):
|
|||
'consistencygroup_id': None,
|
||||
'cgsnapshot_id': None,
|
||||
'group_id': None,
|
||||
'refresh_az': False,
|
||||
'replication_status': 'disabled'}
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
|
@ -273,7 +274,7 @@ class CreateVolumeFlowTestCase(test.TestCase):
|
|||
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,
|
||||
self.assertRaises(exception.InvalidAvailabilityZone,
|
||||
task.execute,
|
||||
self.ctxt,
|
||||
size=1,
|
||||
|
@ -344,6 +345,7 @@ class CreateVolumeFlowTestCase(test.TestCase):
|
|||
'consistencygroup_id': None,
|
||||
'cgsnapshot_id': None,
|
||||
'group_id': None,
|
||||
'refresh_az': True,
|
||||
'replication_status': 'disabled'}
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
|
@ -399,6 +401,7 @@ class CreateVolumeFlowTestCase(test.TestCase):
|
|||
'source_replicaid': None,
|
||||
'consistencygroup_id': None,
|
||||
'cgsnapshot_id': None,
|
||||
'refresh_az': False,
|
||||
'group_id': None, }
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
|
@ -456,6 +459,7 @@ class CreateVolumeFlowTestCase(test.TestCase):
|
|||
'consistencygroup_id': None,
|
||||
'cgsnapshot_id': None,
|
||||
'group_id': None,
|
||||
'refresh_az': False,
|
||||
'replication_status': 'disabled'}
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
|
@ -520,6 +524,7 @@ class CreateVolumeFlowTestCase(test.TestCase):
|
|||
'consistencygroup_id': None,
|
||||
'cgsnapshot_id': None,
|
||||
'group_id': None,
|
||||
'refresh_az': False,
|
||||
'replication_status': 'disabled'}
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
|
@ -585,6 +590,7 @@ class CreateVolumeFlowTestCase(test.TestCase):
|
|||
'consistencygroup_id': None,
|
||||
'cgsnapshot_id': None,
|
||||
'group_id': None,
|
||||
'refresh_az': False,
|
||||
'replication_status': 'disabled'}
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
|
@ -649,6 +655,7 @@ class CreateVolumeFlowTestCase(test.TestCase):
|
|||
'consistencygroup_id': None,
|
||||
'cgsnapshot_id': None,
|
||||
'group_id': None,
|
||||
'refresh_az': False,
|
||||
'replication_status': 'disabled'}
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
|
|
|
@ -39,6 +39,19 @@ class AvailabilityZoneTestCase(base.BaseVolumeTestCase):
|
|||
self.volume_api.list_availability_zones(enable_cache=True)
|
||||
self.assertEqual(1, self.get_all.call_count)
|
||||
|
||||
def test_list_availability_zones_cached_and_refresh_on(self):
|
||||
azs = self.volume_api.list_availability_zones(enable_cache=True,
|
||||
refresh_cache=True)
|
||||
self.assertEqual([{"name": 'a', 'available': True}], list(azs))
|
||||
time_before = self.volume_api.availability_zones_last_fetched
|
||||
self.assertIsNotNone(time_before)
|
||||
self.assertEqual(1, self.get_all.call_count)
|
||||
self.volume_api.list_availability_zones(enable_cache=True,
|
||||
refresh_cache=True)
|
||||
self.assertTrue(time_before !=
|
||||
self.volume_api.availability_zones_last_fetched)
|
||||
self.assertEqual(2, self.get_all.call_count)
|
||||
|
||||
def test_list_availability_zones_no_cached(self):
|
||||
azs = self.volume_api.list_availability_zones(enable_cache=False)
|
||||
self.assertEqual([{"name": 'a', 'available': True}], list(azs))
|
||||
|
|
|
@ -134,12 +134,13 @@ class API(base.Base):
|
|||
self.key_manager = key_manager.API(CONF)
|
||||
super(API, self).__init__(db_driver)
|
||||
|
||||
def list_availability_zones(self, enable_cache=False):
|
||||
def list_availability_zones(self, enable_cache=False, refresh_cache=False):
|
||||
"""Describe the known availability zones
|
||||
|
||||
:retval tuple of dicts, each with a 'name' and 'available' key
|
||||
:param enable_cache: Enable az cache
|
||||
:param refresh_cache: Refresh cache immediately
|
||||
:return: tuple of dicts, each with a 'name' and 'available' key
|
||||
"""
|
||||
refresh_cache = False
|
||||
if enable_cache:
|
||||
if self.availability_zones_last_fetched is None:
|
||||
refresh_cache = True
|
||||
|
@ -347,11 +348,21 @@ class API(base.Base):
|
|||
# taskflow sends out and redirect them to a more useful log for
|
||||
# cinders debugging (or error reporting) usage.
|
||||
with flow_utils.DynamicLogListener(flow_engine, logger=LOG):
|
||||
flow_engine.run()
|
||||
vref = flow_engine.storage.fetch('volume')
|
||||
LOG.info("Create volume request issued successfully.",
|
||||
resource=vref)
|
||||
return vref
|
||||
try:
|
||||
flow_engine.run()
|
||||
vref = flow_engine.storage.fetch('volume')
|
||||
# NOTE(tommylikehu): If the target az is not hit,
|
||||
# refresh the az cache immediately.
|
||||
if flow_engine.storage.fetch('refresh_az'):
|
||||
self.list_availability_zones(enable_cache=True,
|
||||
refresh_cache=True)
|
||||
LOG.info("Create volume request issued successfully.",
|
||||
resource=vref)
|
||||
return vref
|
||||
except exception.InvalidAvailabilityZone:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.list_availability_zones(enable_cache=True,
|
||||
refresh_cache=True)
|
||||
|
||||
@wrap_check_policy
|
||||
def revert_to_snapshot(self, context, volume, snapshot):
|
||||
|
|
|
@ -69,7 +69,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
|
|||
'source_volid', 'volume_type', 'volume_type_id',
|
||||
'encryption_key_id', 'source_replicaid',
|
||||
'consistencygroup_id', 'cgsnapshot_id',
|
||||
'qos_specs', 'group_id'])
|
||||
'qos_specs', 'group_id', 'refresh_az'])
|
||||
|
||||
def __init__(self, image_service, availability_zones, **kwargs):
|
||||
super(ExtractVolumeRequestTask, self).__init__(addons=[ACTION],
|
||||
|
@ -290,6 +290,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
|
|||
the validated availability zone.
|
||||
"""
|
||||
|
||||
refresh_az = False
|
||||
# If the volume will be created in a group, it should be placed in
|
||||
# in same availability zone as the group.
|
||||
if group:
|
||||
|
@ -321,6 +322,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
|
|||
availability_zone = CONF.storage_availability_zone
|
||||
|
||||
if availability_zone not in self.availability_zones:
|
||||
refresh_az = True
|
||||
if CONF.allow_availability_zone_fallback:
|
||||
original_az = availability_zone
|
||||
availability_zone = (
|
||||
|
@ -332,9 +334,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
|
|||
{'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)
|
||||
raise exception.InvalidAvailabilityZone(az=availability_zone)
|
||||
|
||||
# If the configuration only allows cloning to the same availability
|
||||
# zone then we need to enforce that.
|
||||
|
@ -358,7 +358,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
|
|||
"availability zone as the source volume")
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
return availability_zone
|
||||
return availability_zone, refresh_az
|
||||
|
||||
def _get_encryption_key_id(self, key_manager, context, volume_type_id,
|
||||
snapshot, source_volume,
|
||||
|
@ -434,10 +434,8 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
|
|||
image_id,
|
||||
size)
|
||||
|
||||
availability_zone = self._extract_availability_zone(availability_zone,
|
||||
snapshot,
|
||||
source_volume,
|
||||
group)
|
||||
availability_zone, refresh_az = self._extract_availability_zone(
|
||||
availability_zone, snapshot, source_volume, group)
|
||||
|
||||
# TODO(joel-coffman): This special handling of snapshots to ensure that
|
||||
# their volume type matches the source volume is too convoluted. We
|
||||
|
@ -500,6 +498,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
|
|||
'cgsnapshot_id': cgsnapshot_id,
|
||||
'group_id': group_id,
|
||||
'replication_status': replication_status,
|
||||
'refresh_az': refresh_az
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
Now cinder will refresh the az cache immediatelly if previous create
|
||||
volume task failed due to az not found.
|
Loading…
Reference in New Issue