VMAX driver - Heat detach issue.

There is a threading issue in Heat on a delete stack. All
detaches happen simultaneously. The fix locks the portion
of code to determine accurately, the number of volumes left
in the storage group, at any given time, to prevent
attempting to remove a volume that is the last in the
storage group.

Change-Id: I84164a9abac35d962408febbe8d3af759beb6a94
Closes-Bug: #1630535
This commit is contained in:
Helen Walsh 2016-10-05 14:43:06 +01:00
parent ca7cc8077f
commit d9ccfaef8e
2 changed files with 56 additions and 35 deletions

View File

@ -7853,7 +7853,7 @@ class EMCVMAXMaskingTest(test.TestCase):
volumeInstance, volumeName, sgGroupName, extraSpecs)
self.assertIsNone(msg)
def test_remove_volume_from_sg(self):
def test_cleanup_deletion_v3(self):
masking = self.driver.common.masking
conn = self.fake_ecom_connection()
volumeInstanceName = (
@ -7982,6 +7982,30 @@ class EMCVMAXMaskingTest(test.TestCase):
rollbackDict))
self.assertEqual(expectedmessage, message)
def test_remove_volume_from_sg(self):
extraSpecs = self.data.extra_specs
conn = self.fake_ecom_connection()
common = self.driver.common
masking = common.masking
controllerConfigService = (
common.utils.find_controller_configuration_service(
conn, self.data.storage_system))
storageGroupName = self.data.storagegroupname
storageGroupInstanceName = (
self.driver.utils.find_storage_masking_group(
conn, controllerConfigService, storageGroupName))
volumeInstanceNames = (
conn.EnumerateInstanceNames("EMC_StorageVolume"))
volumeInstanceName = volumeInstanceNames[0]
volumeInstance = conn.GetInstance(volumeInstanceName)
masking.get_devices_from_storage_group = (
mock.Mock(return_value=volumeInstanceNames))
masking._remove_volume_from_sg(
conn, controllerConfigService, storageGroupInstanceName,
volumeInstance, extraSpecs)
masking.get_devices_from_storage_group.assert_called_with(
conn, storageGroupInstanceName)
class EMCVMAXFCTest(test.TestCase):
def setUp(self):

View File

@ -1903,28 +1903,31 @@ class EMCVMAXMasking(object):
instance = conn.GetInstance(storageGroupInstanceName, LocalOnly=False)
storageGroupName = instance['ElementName']
volumeInstanceNames = self.get_devices_from_storage_group(
conn, storageGroupInstanceName)
@lockutils.synchronized(storageGroupName + 'remove',
"emc-remove-sg", True)
def do_remove_volume_from_sg():
volumeInstanceNames = self.get_devices_from_storage_group(
conn, storageGroupInstanceName)
numVolInStorageGroup = len(volumeInstanceNames)
LOG.debug(
"There are %(numVol)d volumes in the storage group "
"%(maskingGroup)s.",
{'numVol': numVolInStorageGroup,
'maskingGroup': storageGroupInstanceName})
numVolInStorageGroup = len(volumeInstanceNames)
LOG.debug(
"There are %(numVol)d volumes in the storage group "
"%(maskingGroup)s.",
{'numVol': numVolInStorageGroup,
'maskingGroup': storageGroupInstanceName})
if numVolInStorageGroup == 1:
# Last volume in the storage group.
self._last_vol_in_SG(
conn, controllerConfigService, storageGroupInstanceName,
storageGroupName, volumeInstance,
volumeInstance['ElementName'], extraSpecs)
else:
# Not the last volume so remove it from storage group
self._multiple_vols_in_SG(
conn, controllerConfigService, storageGroupInstanceName,
volumeInstance, volumeInstance['ElementName'],
numVolInStorageGroup, extraSpecs)
if numVolInStorageGroup == 1:
# Last volume in the storage group.
self._last_vol_in_SG(
conn, controllerConfigService, storageGroupInstanceName,
storageGroupName, volumeInstance,
volumeInstance['ElementName'], extraSpecs)
else:
# Not the last volume so remove it from storage group
self._multiple_vols_in_SG(
conn, controllerConfigService, storageGroupInstanceName,
volumeInstance, volumeInstance['ElementName'],
numVolInStorageGroup, extraSpecs)
return do_remove_volume_from_sg()
def _last_vol_in_SG(
self, conn, controllerConfigService, storageGroupInstanceName,
@ -1958,16 +1961,18 @@ class EMCVMAXMasking(object):
LOG.debug("Unable to get masking view %(maskingView)s "
"from storage group.",
{'maskingView': mvInstanceName})
# Remove the volume from the storage group and delete the SG.
self._remove_last_vol_and_delete_sg(
conn, controllerConfigService,
storageGroupInstanceName,
storageGroupName, volumeInstance.path,
volumeName, extraSpecs)
status = True
else:
maskingViewInstance = conn.GetInstance(
mvInstanceName, LocalOnly=False)
maskingViewName = maskingViewInstance['ElementName']
if mvInstanceName:
maskingViewInstance = conn.GetInstance(
mvInstanceName, LocalOnly=False)
maskingViewName = maskingViewInstance['ElementName']
@lockutils.synchronized(maskingViewName,
"emc-mv-", True)
def do_delete_mv_ig_and_sg():
@ -1978,14 +1983,6 @@ class EMCVMAXMasking(object):
extraSpecs)
do_delete_mv_ig_and_sg()
status = True
else:
# Remove the volume from the storage group and delete the SG.
self._remove_last_vol_and_delete_sg(
conn, controllerConfigService,
storageGroupInstanceName,
storageGroupName, volumeInstance.path,
volumeName, extraSpecs)
status = True
return status
def _multiple_vols_in_SG(