Merge "PowerMax driver - Rapid TDEV Deallocation"

This commit is contained in:
Zuul 2019-06-26 18:50:06 +00:00 committed by Gerrit Code Review
commit a73b1a940a
9 changed files with 103 additions and 29 deletions

View File

@ -752,6 +752,8 @@ class PowerMaxData(object):
headroom = {'headroom': [{'headroomCapacity': 20348.29}]}
ucode_5978_foxtail = {'ucode': '5978.435.435'}
p_vol_rest_response_single = {
'id': 'f3aab01c-a5a8-4fb4-af2b-16ae1c46dc9e_0', 'count': 1,
'expirationTime': 1521650650793, 'maxPageSize': 1000,

View File

@ -561,20 +561,39 @@ class PowerMaxRestTest(test.TestCase):
mock_modify.assert_called_once_with(
self.data.array, device_id, extend_vol_payload)
def test_delete_volume(self):
def test_legacy_delete_volume(self):
device_id = self.data.device_id
vb_except = exception.VolumeBackendAPIException
with mock.patch.object(self.rest, 'delete_resource') as mock_delete, \
with mock.patch.object(self.rest, 'delete_resource') as mock_delete, (
mock.patch.object(
self.rest, '_modify_volume',
side_effect=[None, None, None, vb_except]) as mock_modify:
for x in range(0, 2):
side_effect=[None, None, None, vb_except])) as mock_modify:
for _ in range(0, 2):
self.rest.delete_volume(self.data.array, device_id)
mod_call_count = mock_modify.call_count
self.assertEqual(4, mod_call_count)
mock_delete.assert_called_once_with(
self.data.array, 'sloprovisioning', 'volume', device_id)
def test_delete_volume(self):
device_id = self.data.device_id
ucode_5978_foxtail = tpd.PowerMaxData.ucode_5978_foxtail
with mock.patch.object(
self.rest, 'delete_resource') as mock_delete, (
mock.patch.object(
self.rest, '_modify_volume')) as mock_modify, (
mock.patch.object(
self.rest, 'get_array_detail',
return_value=ucode_5978_foxtail))as mock_det:
self.rest.delete_volume(self.data.array, device_id)
detail_call_count = mock_det.call_count
mod_call_count = mock_modify.call_count
self.assertEqual(1, detail_call_count)
self.assertEqual(1, mod_call_count)
mock_delete.assert_called_once_with(
self.data.array, 'sloprovisioning', 'volume', device_id)
def test_rename_volume(self):
device_id = self.data.device_id
payload = {'editVolumeActionParam': {
@ -1369,14 +1388,14 @@ class PowerMaxRestTest(test.TestCase):
ref_payload2 = {'establish': 'true', 'rdfType': 'RDF1'}
payload3 = self.rest.get_metro_payload_info(
self.data.array, ref_payload2, self.data.rdf_group_no, {})
ref_payload3 = {'rdfType': 'NA', 'format': 'true'}
ref_payload3 = {'rdfType': 'RDF1', 'format': 'true'}
self.assertEqual(ref_payload3, payload3)
def test_modify_rdf_device_pair(self):
resource_name = '70/volume/00001'
common_opts = {'force': 'false', 'symForce': 'false', 'star': 'false',
'hop2': 'false', 'bypass': 'false'}
suspend_payload = {'action': 'Suspend',
suspend_payload = {'action': 'SUSPEND',
'executionOption': 'ASYNCHRONOUS',
'suspend': common_opts}

View File

@ -1883,8 +1883,12 @@ class PowerMaxCommon(object):
"""
# Cleanup remote replication
if self.utils.is_replication_enabled(extra_specs):
# Add force to allow volume removal from RDF enabled storage groups
extra_specs['force_vol_remove'] = True
self.cleanup_lun_replication(volume, volume_name,
device_id, extra_specs)
# Remove from any storage groups
self.masking.remove_and_reset_members(
array, volume, device_id, volume_name, extra_specs, False)
@ -3486,7 +3490,8 @@ class PowerMaxCommon(object):
try:
self.provision.get_or_create_group(array, group_name, extra_specs)
self.masking.add_volume_to_storage_group(
array, device_id, group_name, volume_name, extra_specs)
array, device_id, group_name, volume_name, extra_specs,
force=True)
# Add remote volume
self.provision.get_or_create_group(
remote_array, group_name, extra_specs)
@ -3622,6 +3627,7 @@ class PowerMaxCommon(object):
array, device_id, target_device, rdf_group,
rep_extra_specs, metro_grp)
# Remove the volume from the metro_grp
rep_extra_specs['force_vol_remove'] = True
self.masking.remove_volume_from_sg(array, device_id, 'metro_vol',
metro_grp, rep_extra_specs)
# Resume I/O on the RDF links for any remaining volumes
@ -4089,7 +4095,8 @@ class PowerMaxCommon(object):
message=exception_message)
self.masking.add_volume_to_storage_group(
array, device_id, storagegroup_name, volume_name, extra_specs)
array, device_id, storagegroup_name, volume_name, extra_specs,
force=True)
return storagegroup_name

View File

@ -109,6 +109,7 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
- Support for storage-assisted in-use retype
(bp/powermax-storage-assisted-inuse-retype)
4.1.0 - Changing from 90 to 91 rest endpoints
- Support for Rapid TDEV Delete (bp powermax-tdev-deallocation)
"""
VERSION = "4.1.0"

View File

@ -114,6 +114,7 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
- Support for storage-assisted in-use retype
(bp/powermax-storage-assisted-inuse-retype)
4.1.0 - Changing from 90 to 91 rest endpoints
- Support for Rapid TDEV Delete (bp powermax-tdev-deallocation)
"""
VERSION = "4.1.0"

View File

@ -632,7 +632,7 @@ class PowerMaxMasking(object):
def add_volume_to_storage_group(
self, serial_number, device_id, storagegroup_name,
volume_name, extra_specs):
volume_name, extra_specs, force=False):
"""Add a volume to a storage group.
:param serial_number: array serial number
@ -640,6 +640,7 @@ class PowerMaxMasking(object):
:param storagegroup_name: storage group name
:param volume_name: volume name
:param extra_specs: extra specifications
:param force: add force argument to call
"""
start_time = time.time()
@ -655,7 +656,7 @@ class PowerMaxMasking(object):
'sg_name': storagegroup_name})
else:
self.rest.add_vol_to_sg(serial_number, sg_name,
device_id, extra_specs)
device_id, extra_specs, force)
do_add_volume_to_sg(storagegroup_name, serial_number)
LOG.debug("Add volume to storagegroup took: %(delta)s H:MM:SS.",

View File

@ -931,21 +931,28 @@ class PowerMaxRest(object):
found_device_id = device_id
return found_device_id
def add_vol_to_sg(self, array, storagegroup_name, device_id, extra_specs):
def add_vol_to_sg(self, array, storagegroup_name, device_id, extra_specs,
force=False):
"""Add a volume to a storage group.
:param array: the array serial number
:param storagegroup_name: storage group name
:param device_id: the device id
:param extra_specs: extra specifications
:param force: add force argument to call
"""
if not isinstance(device_id, list):
device_id = [device_id]
force_add = "true" if force else "false"
payload = ({"executionOption": "ASYNCHRONOUS",
"editStorageGroupActionParam": {
"expandStorageGroupParam": {
"addSpecificVolumeParam": {
"volumeId": device_id}}}})
"volumeId": device_id,
"remoteSymmSGInfoParam": {
"force": force_add}}}}})
status_code, job = self.modify_storage_group(
array, storagegroup_name, payload)
@ -961,12 +968,17 @@ class PowerMaxRest(object):
:param device_id: the device id
:param extra_specs: the extra specifications
"""
force_vol_remove = ("true" if "force_vol_remove" in extra_specs
else "false")
if not isinstance(device_id, list):
device_id = [device_id]
payload = ({"executionOption": "ASYNCHRONOUS",
"editStorageGroupActionParam": {
"removeVolumeParam": {
"volumeId": device_id}}})
"volumeId": device_id,
"remoteSymmSGInfoParam": {
"force": force_vol_remove}}}})
status_code, job = self.modify_storage_group(
array, storagegroup_name, payload)
@ -1267,23 +1279,47 @@ class PowerMaxRest(object):
self._modify_volume(array, device_id, rename_vol_payload)
def delete_volume(self, array, device_id):
"""Deallocate or delete a volume.
"""Delete a volume.
:param array: the array serial number
:param device_id: volume device id
"""
# Deallocate volume. Can fail if there are no tracks allocated.
payload = {"editVolumeActionParam": {
"freeVolumeParam": {"free_volume": 'true'}}}
try:
self._modify_volume(array, device_id, payload)
# Rename volume, removing the OS-<cinderUUID>
self.rename_volume(array, device_id, None)
except Exception as e:
LOG.warning('Deallocate volume failed with %(e)s.'
'Attempting delete.', {'e': e})
# Try to delete the volume if deallocate failed.
self.delete_resource(array, SLOPROVISIONING, "volume", device_id)
array_details = self.get_array_detail(array)
ucode_major_level = 0
ucode_minor_level = 0
if array_details:
split_ucode_level = array_details['ucode'].split('.')
ucode_level = [int(level) for level in split_ucode_level]
ucode_major_level = ucode_level[0]
ucode_minor_level = ucode_level[1]
if ((ucode_major_level >= utils.UCODE_5978)
and (ucode_minor_level > utils.UCODE_5978_ELMSR)):
# Use Rapid TDEV Deallocation to delete after ELMSR
try:
# Rename volume, removing the OS-<cinderUUID>
self.rename_volume(array, device_id, None)
self.delete_resource(array, SLOPROVISIONING,
"volume", device_id)
except Exception as e:
LOG.warning('Delete volume failed with %(e)s.', {'e': e})
raise
else:
# Pre-Foxtail, deallocation and delete are separate calls
payload = {"editVolumeActionParam": {
"freeVolumeParam": {"free_volume": 'true'}}}
try:
# Rename volume, removing the OS-<cinderUUID>
self.rename_volume(array, device_id, None)
self._modify_volume(array, device_id, payload)
pass
except Exception as e:
LOG.warning('Deallocate volume failed with %(e)s.'
'Attempting delete.', {'e': e})
# Try to delete the volume if deallocate failed.
self.delete_resource(array, SLOPROVISIONING,
"volume", device_id)
def find_mv_connections_for_vol(self, array, maskingview, device_id):
"""Find the host_lun_id for a volume in a masking view.
@ -2324,7 +2360,7 @@ class PowerMaxRest(object):
# Need to format subsequent volumes
payload['format'] = 'true'
payload.pop('establish')
payload['rdfType'] = 'NA'
payload['rdfType'] = 'RDF1'
return payload
def modify_rdf_device_pair(
@ -2347,7 +2383,7 @@ class PowerMaxRest(object):
and extra_specs[utils.REP_MODE] == utils.REP_ASYNC):
common_opts.update({"immediate": 'false',
"consExempt": 'true'})
payload = {"action": "Suspend",
payload = {"action": "SUSPEND",
"executionOption": "ASYNCHRONOUS",
"suspend": common_opts}
@ -2379,7 +2415,7 @@ class PowerMaxRest(object):
resource_name = ("%(rdf_num)s/volume/%(device_id)s"
% {'rdf_num': rdf_group, 'device_id': device_id})
self.delete_resource(array, REPLICATION, 'rdf_group', resource_name,
private="/private", params=params)
params=params)
def get_storage_group_rep(self, array, storage_group_name):
"""Given a name, return storage group details wrt replication.

View File

@ -41,6 +41,8 @@ VMAX_AFA_MODELS = ['VMAX250F', 'VMAX450F', 'VMAX850F', 'VMAX950F']
MAX_SRP_LENGTH = 16
TRUNCATE_5 = 5
TRUNCATE_27 = 27
UCODE_5978_ELMSR = 221
UCODE_5978 = 5978
ARRAY = 'array'
SLO = 'slo'

View File

@ -0,0 +1,5 @@
---
features:
- |
PowerMax driver - Volume deallocate and volume delete functionality
have been combined into a single workflow.