VMAX Driver - QoS support for the VMAX3
Quality of Service is regulating the throughput/bandwidth that you use in provisioning storage. You might want to ensure that your critical workloads get the best performance possible, so you use Storage QoS to limit the throughput to non-critical workloads. Change-Id: I78c454692c4f7ddc7a5fa863205bcc68323bfa4b Implements: blueprint vmax-qos
This commit is contained in:
parent
451e4fcda4
commit
a569e682f0
@ -726,7 +726,7 @@ class FakeEcomConnection(object):
|
||||
return result
|
||||
|
||||
def ModifyInstance(self, objectpath, PropertyList=None):
|
||||
pass
|
||||
pass
|
||||
|
||||
def DeleteInstance(self, objectpath):
|
||||
pass
|
||||
@ -1164,6 +1164,14 @@ class FakeEcomConnection(object):
|
||||
else:
|
||||
targetmaskinggroup['ElementName'] = (
|
||||
self.data.storagegroupname)
|
||||
if 'EMCMaximumIO' in objectpath:
|
||||
targetmaskinggroup['EMCMaximumIO'] = objectpath['EMCMaximumIO']
|
||||
if 'EMCMaximumBandwidth' in objectpath:
|
||||
targetmaskinggroup['EMCMaximumBandwidth'] = (
|
||||
objectpath['EMCMaximumBandwidth'])
|
||||
if 'EMCMaxIODynamicDistributionType' in objectpath:
|
||||
targetmaskinggroup['EMCMaxIODynamicDistributionType'] = (
|
||||
objectpath['EMCMaxIODynamicDistributionType'])
|
||||
return targetmaskinggroup
|
||||
|
||||
def _getinstance_unit(self, objectpath):
|
||||
@ -6242,8 +6250,8 @@ class EMCV3DriverTestCase(test.TestCase):
|
||||
'get_volume_type_extra_specs',
|
||||
return_value={'volume_backend_name': 'V3_BE'})
|
||||
def test_create_cgsnapshot_v3_success(
|
||||
self, _mock_volume_type, _mock_storage, _mock_cg, _mock_members,
|
||||
mock_rg):
|
||||
self, _mock_volume_type, _mock_storage, _mock_cg,
|
||||
_mock_members, mock_rg):
|
||||
provisionv3 = self.driver.common.provisionv3
|
||||
provisionv3.create_group_replica = mock.Mock(return_value=(0, None))
|
||||
self.driver.create_cgsnapshot(
|
||||
@ -6448,6 +6456,9 @@ class EMCV3DriverTestCase(test.TestCase):
|
||||
targetInstance = (
|
||||
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||
deviceID = targetInstance['DeviceID']
|
||||
common._delete_from_pool_v3(storageConfigService, targetInstance,
|
||||
targetInstance['Name'], deviceID,
|
||||
extraSpecs)
|
||||
common._delete_from_pool_v3.assert_called_with(storageConfigService,
|
||||
targetInstance,
|
||||
targetInstance['Name'],
|
||||
@ -7162,7 +7173,6 @@ class EMCV2MultiPoolDriverMultipleEcomsTestCase(test.TestCase):
|
||||
self.fake_sleep)
|
||||
self.stubs.Set(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
|
||||
self.fake_is_v3)
|
||||
|
||||
driver = emc_vmax_fc.EMCVMAXFCDriver(configuration=configuration)
|
||||
driver.db = FakeDB()
|
||||
driver.common.conn = FakeEcomConnection()
|
||||
@ -8001,6 +8011,51 @@ class EMCVMAXUtilsTest(test.TestCase):
|
||||
self.driver.utils.get_ratio_from_max_sub_per(str(0)))
|
||||
self.assertIsNone(max_subscription_percent_float)
|
||||
|
||||
def test_update_storage_QOS(self):
|
||||
conn = FakeEcomConnection()
|
||||
pywbem = mock.Mock()
|
||||
pywbem.cim_obj = mock.Mock()
|
||||
pywbem.cim_obj.CIMInstance = mock.Mock()
|
||||
emc_vmax_utils.pywbem = pywbem
|
||||
|
||||
extraSpecs = {'volume_backend_name': 'V3_BE',
|
||||
'qos': {
|
||||
'maxIOPS': '6000',
|
||||
'maxMBPS': '6000',
|
||||
'DistributionType': 'Always'
|
||||
}}
|
||||
|
||||
storageGroupInstanceName = {
|
||||
'CreationClassName': 'CIM_DeviceMaskingGroup',
|
||||
'EMCMaximumIO': 6000,
|
||||
'EMCMaximumBandwidth': 5000,
|
||||
'EMCMaxIODynamicDistributionType': 1
|
||||
|
||||
}
|
||||
modifiedstorageGroupInstance = {
|
||||
'CreationClassName': 'CIM_DeviceMaskingGroup',
|
||||
'EMCMaximumIO': 6000,
|
||||
'EMCMaximumBandwidth': 6000,
|
||||
'EMCMaxIODynamicDistributionType': 1
|
||||
|
||||
}
|
||||
conn.ModifyInstance = (
|
||||
mock.Mock(return_value=modifiedstorageGroupInstance))
|
||||
self.driver.common.utils.update_storagegroup_qos(
|
||||
conn, storageGroupInstanceName, extraSpecs)
|
||||
|
||||
modifiedInstance = self.driver.common.utils.update_storagegroup_qos(
|
||||
conn, storageGroupInstanceName, extraSpecs)
|
||||
self.assertIsNotNone(modifiedInstance)
|
||||
self.assertEqual(
|
||||
6000, modifiedInstance['EMCMaximumIO'])
|
||||
self.assertEqual(
|
||||
6000, modifiedInstance['EMCMaximumBandwidth'])
|
||||
self.assertEqual(
|
||||
1, modifiedInstance['EMCMaxIODynamicDistributionType'])
|
||||
self.assertEqual('CIM_DeviceMaskingGroup',
|
||||
modifiedInstance['CreationClassName'])
|
||||
|
||||
|
||||
class EMCVMAXCommonTest(test.TestCase):
|
||||
def setUp(self):
|
||||
|
@ -1306,6 +1306,7 @@ class EMCVMAXCommon(object):
|
||||
:returns: string -- configuration file
|
||||
"""
|
||||
extraSpecs = self.utils.get_volumetype_extraspecs(volume, volumeTypeId)
|
||||
qosSpecs = self.utils.get_volumetype_qosspecs(volume, volumeTypeId)
|
||||
configGroup = None
|
||||
|
||||
# If there are no extra specs then the default case is assumed.
|
||||
@ -1313,8 +1314,7 @@ class EMCVMAXCommon(object):
|
||||
configGroup = self.configuration.config_group
|
||||
configurationFile = self._register_config_file_from_config_group(
|
||||
configGroup)
|
||||
|
||||
return extraSpecs, configurationFile
|
||||
return extraSpecs, configurationFile, qosSpecs
|
||||
|
||||
def _get_ecom_connection(self):
|
||||
"""Get the ecom connection.
|
||||
@ -1751,7 +1751,7 @@ class EMCVMAXCommon(object):
|
||||
:raises: VolumeBackendAPIException
|
||||
"""
|
||||
try:
|
||||
extraSpecs, configurationFile = (
|
||||
extraSpecs, configurationFile, qosSpecs = (
|
||||
self._set_config_file_and_get_extra_specs(
|
||||
volume, volumeTypeId))
|
||||
|
||||
@ -1777,6 +1777,9 @@ class EMCVMAXCommon(object):
|
||||
else:
|
||||
# V2 extra specs
|
||||
extraSpecs = self._set_v2_extra_specs(extraSpecs, poolRecord)
|
||||
if (qosSpecs.get('qos_spec')
|
||||
and qosSpecs['qos_specs']['consumer'] != "front-end"):
|
||||
extraSpecs['qos'] = qosSpecs['qos_specs']['specs']
|
||||
except Exception:
|
||||
import sys
|
||||
exceptionMessage = (_(
|
||||
@ -2893,6 +2896,10 @@ class EMCVMAXCommon(object):
|
||||
LOG.error(exceptionMessage)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exceptionMessage)
|
||||
# If qos exists, update storage group to reflect qos parameters
|
||||
if 'qos' in extraSpecs:
|
||||
self.utils.update_storagegroup_qos(
|
||||
self.conn, defaultStorageGroupInstanceName, extraSpecs)
|
||||
|
||||
self._add_volume_to_default_storage_group_on_create(
|
||||
volumeDict, volumeName, storageConfigService,
|
||||
@ -2981,6 +2988,10 @@ class EMCVMAXCommon(object):
|
||||
sgInstanceName = self.provisionv3.create_storage_group_v3(
|
||||
self.conn, controllerConfigService, storageGroupName,
|
||||
poolName, slo, workload, extraSpecs)
|
||||
# If qos exists, update storage group to reflect qos parameters
|
||||
if 'qos' in extraSpecs:
|
||||
self.utils.update_storagegroup_qos(
|
||||
self.conn, sgInstanceName, extraSpecs)
|
||||
|
||||
return sgInstanceName
|
||||
|
||||
|
@ -70,6 +70,7 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
|
||||
2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634)
|
||||
- SnapVX licensing checks for VMAX3 (bug #1587017)
|
||||
- VMAX oversubscription Support (blueprint vmax-oversubscription)
|
||||
- QoS support (blueprint vmax-qos)
|
||||
"""
|
||||
|
||||
VERSION = "2.4.0"
|
||||
|
@ -76,6 +76,7 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
|
||||
2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634)
|
||||
- SnapVX licensing checks for VMAX3 (bug #1587017)
|
||||
- VMAX oversubscription Support (blueprint vmax-oversubscription)
|
||||
- QoS support (blueprint vmax-qos)
|
||||
|
||||
"""
|
||||
|
||||
|
@ -1083,6 +1083,28 @@ class EMCVMAXUtils(object):
|
||||
|
||||
return extraSpecs
|
||||
|
||||
def get_volumetype_qosspecs(self, volume, volumeTypeId=None):
|
||||
"""Get the qos specs.
|
||||
|
||||
:param volume: the volume dictionary
|
||||
:param volumeTypeId: Optional override for volume['volume_type_id']
|
||||
:returns: dict -- qosSpecs - the qos specs
|
||||
"""
|
||||
qosSpecs = {}
|
||||
|
||||
try:
|
||||
if volumeTypeId:
|
||||
type_id = volumeTypeId
|
||||
else:
|
||||
type_id = volume['volume_type_id']
|
||||
if type_id is not None:
|
||||
qosSpecs = volume_types.get_volume_type_qos_specs(type_id)
|
||||
|
||||
except Exception:
|
||||
LOG.debug("Unable to get QoS specifications.")
|
||||
|
||||
return qosSpecs
|
||||
|
||||
def get_volume_type_name(self, volume):
|
||||
"""Get the volume type name.
|
||||
|
||||
@ -2645,3 +2667,51 @@ class EMCVMAXUtils(object):
|
||||
max_over_sub_ratio = float(max_sub_ratio_from_per)
|
||||
|
||||
return max_over_sub_ratio
|
||||
|
||||
def update_storagegroup_qos(self, conn, storagegroup, extraspecs):
|
||||
"""Update the storagegroupinstance with qos details.
|
||||
|
||||
If MaxIOPS or maxMBPS is in extraspecs, then DistributionType can be
|
||||
modified in addition to MaxIOPS or/and maxMBPS
|
||||
If MaxIOPS or maxMBPS is NOT in extraspecs, we check to see if
|
||||
either is set in StorageGroup. If so, then DistributionType can be
|
||||
modified
|
||||
|
||||
:param conn: connection to the ecom server
|
||||
:param storagegroup: the storagegroup instance name
|
||||
:param extraSpecs: extra specifications
|
||||
"""
|
||||
if type(storagegroup) is pywbem.cim_obj.CIMInstance:
|
||||
storagegroupInstance = storagegroup
|
||||
else:
|
||||
storagegroupInstance = conn.GetInstance(storagegroup)
|
||||
propertylist = []
|
||||
if 'maxIOPS' in extraspecs.get('qos'):
|
||||
maxiops = self.get_num(extraspecs.get('qos').get('maxIOPS'), '32')
|
||||
if maxiops != storagegroupInstance['EMCMaximumIO']:
|
||||
storagegroupInstance['EMCMaximumIO'] = maxiops
|
||||
propertylist.append('EMCMaximumIO')
|
||||
if 'maxMBPS' in extraspecs.get('qos'):
|
||||
maxmbps = self.get_num(extraspecs.get('qos').get('maxMBPS'), '32')
|
||||
if maxmbps != storagegroupInstance['EMCMaximumBandwidth']:
|
||||
storagegroupInstance['EMCMaximumBandwidth'] = maxmbps
|
||||
propertylist.append('EMCMaximumBandwidth')
|
||||
if 'DistributionType' in extraspecs.get('qos') and (
|
||||
propertylist or (
|
||||
storagegroupInstance['EMCMaximumBandwidth'] != 0) or (
|
||||
storagegroupInstance['EMCMaximumIO'] != 0)):
|
||||
dynamicdict = {'never': 1, 'onfailure': 2, 'always': 3}
|
||||
dynamicvalue = dynamicdict.get(
|
||||
extraspecs.get('qos').get('DistributionType').lower())
|
||||
if dynamicvalue:
|
||||
distributiontype = self.get_num(dynamicvalue, '16')
|
||||
if distributiontype != (
|
||||
storagegroupInstance['EMCMaxIODynamicDistributionType']
|
||||
):
|
||||
storagegroupInstance['EMCMaxIODynamicDistributionType'] = (
|
||||
distributiontype)
|
||||
propertylist.append('EMCMaxIODynamicDistributionType')
|
||||
if propertylist:
|
||||
modifiedInstance = conn.ModifyInstance(storagegroupInstance,
|
||||
PropertyList=propertylist)
|
||||
return modifiedInstance
|
||||
|
3
releasenotes/notes/vmax-qos-eb40ed35bd2f457d.yaml
Normal file
3
releasenotes/notes/vmax-qos-eb40ed35bd2f457d.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- QoS support for the VMAX.
|
Loading…
Reference in New Issue
Block a user