Merge "PowerMax driver - Rapid TDEV Deallocation"
This commit is contained in:
commit
a73b1a940a
|
@ -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,
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
PowerMax driver - Volume deallocate and volume delete functionality
|
||||
have been combined into a single workflow.
|
Loading…
Reference in New Issue