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:
parent
ca7cc8077f
commit
d9ccfaef8e
|
@ -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):
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue