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:
wangxiyuan 2017-05-24 15:05:43 +08:00 committed by TommyLike
parent 40f9e53a6b
commit a85ae95a99
8 changed files with 59 additions and 20 deletions

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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):

View File

@ -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
}

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Now cinder will refresh the az cache immediatelly if previous create
volume task failed due to az not found.