Skip to content

Commit 3541688

Browse files
author
Peter Wang
committed
Add Qos support for Unity Cinder driver
This commit allows QoS (maxIOPS, maxBWS) to be set on Unity volume. To enable this function, User needs to set `maxIOPS` and/or `maxBWS` on qos specs and associate it with a volume type.
1 parent 0b9c612 commit 3541688

2 files changed

Lines changed: 85 additions & 6 deletions

File tree

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,16 @@ Over subscription allows that the sum of all volumes' capacity (provisioned capa
187187
`max_over_subscription_ratio` in the back-end section is the ratio of provisioned capacity over total capacity.
188188

189189
The default value of `max_over_subscription_ratio` is 20.0, which means the provisioned capacity can be 20 times of the total capacity. If the value of this ratio is set larger than 1.0, the provisioned capacity can exceed the total capacity.
190+
191+
## QoS support
192+
193+
Unity driver now supports QoS. To enable this function, User needs to set
194+
`maxIOPS` and/or `maxBWS` on QoS specs and associate it with a volume type.
195+
196+
Example:
197+
198+
cinder qos-create unity_qos consumer=”back-end” maxIOPS=1000 maxBWS=1000
199+
cinder qos-associate <qos-spec-id> <volume-type-id>
200+
201+
202+
NOTE: Unity driver only supports QoS spec whose consumer is set to `back-end`.

emc_unity.py

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1313
# License for the specific language governing permissions and limitations
1414
# under the License.
15+
1516
"""
1617
Drivers for EMC Unity array based on RESTful API.
1718
"""
@@ -45,12 +46,17 @@
4546
LOG = logging.getLogger(__name__)
4647

4748
CONF = cfg.CONF
48-
VERSION = '00.02.01'
49+
VERSION = '00.02.02'
4950

5051
GiB = 1024 * 1024 * 1024
5152
ENABLE_TRACE = False
5253
EMC_OPENSTACK_DUMMY_LUN = 'openstack_dummy_lun'
5354

55+
QOS_MAX_IOPS = 'maxIOPS'
56+
QOS_MAX_BWS = 'maxBWS'
57+
QOS_CONSUMER_BACKEND = 'back-end'
58+
QOS_CONSUMER_FRONTEND = 'front-end'
59+
5460
loc_opts = [
5561
cfg.StrOpt('storage_pool_names',
5662
default=None,
@@ -123,6 +129,7 @@ class EMCUnityRESTClient(object):
123129
HostLUNAccessEnum_NoAccess = 0
124130
HostLUNAccessEnum_Production = 1
125131
LUN_NAME_IN_USE = 108007744
132+
POLICY_NAME_IN_USE = 151032071
126133

127134
def __init__(self, host, port=443, user='Local/admin',
128135
password='', realm='Security Realm',
@@ -349,13 +356,17 @@ def update_consistencygroup(self, group_id, add_luns=None,
349356
err, resp = self._request(cg_update_url, req_data)
350357
return err, resp
351358

352-
def create_lun(self, pool_id, name, size, **kwargs):
359+
def create_lun(self, pool_id, name, size, is_thin=False,
360+
limit_policy_id=None):
353361
lun_create_url = '/api/types/storageResource/action/createLun'
354362
lun_parameters = {'pool': {"id": pool_id},
355363
'isThinEnabled': True,
356364
'size': size}
357-
if 'is_thin' in kwargs:
358-
lun_parameters['isThinEnabled'] = kwargs['is_thin']
365+
if is_thin:
366+
lun_parameters['isThinEnabled'] = is_thin
367+
if limit_policy_id:
368+
lun_parameters['ioLimitParameters'] = {
369+
'ioLimitPolicy': {'id': limit_policy_id}}
359370
# More Advance Feature
360371
data = {'name': name,
361372
'description': name,
@@ -534,6 +545,33 @@ def modify_lun_name(self, lun_id, new_name):
534545
raise exception.VolumeBackendAPIException(
535546
data=reason)
536547

548+
def get_limit_policy(self, name):
549+
"""Get existing IO limits by name."""
550+
return self._filter_by_field(
551+
'ioLimitPolicy', 'name', name,
552+
('id', 'maxIOPS::ioLimitRules.maxIOPS',
553+
'maxKBPS::ioLimitRules.maxKBPS'))
554+
555+
def create_limit_policy(self, name, max_iops=None, max_kbps=None):
556+
"""Create host IO limits."""
557+
create_limit_url = \
558+
'/api/types/ioLimitPolicy/instances'
559+
data = {'name': name, 'ioLimitRules': []}
560+
rule = {}
561+
if max_iops:
562+
rule.update({'maxIOPS': max_iops})
563+
if max_kbps:
564+
rule.update({'maxKBPS': max_kbps})
565+
rule.update({'name': name})
566+
data['ioLimitRules'].append(rule)
567+
err, resp = self._request(create_limit_url, data)
568+
if err:
569+
if err['errorCode'] == self.POLICY_NAME_IN_USE:
570+
return self.get_limit_policy(name)[0]
571+
else:
572+
raise exception.VolumeBackendAPIException(err['messages'])
573+
return resp['content']
574+
537575

538576
class ArrangeHostTask(task.Task):
539577
def __init__(self, helper, connector):
@@ -748,6 +786,22 @@ def _get_volumetype_extraspecs(self, volume):
748786

749787
return specs
750788

789+
def _get_qos_specs(self, volume_type):
790+
qos_specs_id = (None if not volume_type
791+
else volume_type['qos_specs_id'])
792+
emc_qos = {}
793+
if not qos_specs_id:
794+
return {}, None
795+
if qos_specs_id:
796+
qos_specs = volume_type['qos_specs']
797+
for qos_spec in qos_specs:
798+
emc_qos[qos_spec['key']] = qos_spec['value']
799+
800+
if emc_qos['consumer'] == QOS_CONSUMER_FRONTEND:
801+
# We do not handle front-end qos specs
802+
return {}, None
803+
return emc_qos, qos_specs_id
804+
751805
def _load_provider_location(self, provider_location):
752806
pl_dict = {}
753807
for item in provider_location.split('|'):
@@ -863,7 +917,7 @@ def create_cgsnapshot(self, cgsnapshot, snapshots):
863917
snap_name)
864918
else:
865919
raise exception.VolumeBackendAPIException(data=err['messages'])
866-
model_update = {'status': cgsnapshot['status']}
920+
model_update = {'status': 'available'}
867921
for snapshot in snapshots:
868922
snapshot['status'] = 'available'
869923
return model_update, snapshots
@@ -891,6 +945,7 @@ def create_volume(self, volume):
891945
name = volume['name']
892946
size = volume['size'] * GiB
893947
extra_specs = self._get_volumetype_extraspecs(volume)
948+
qos_specs, qos_specs_id = self._get_qos_specs(volume['volume_type'])
894949
k = 'storagetype:provisioning'
895950
is_thin = False
896951
if k in extra_specs:
@@ -903,9 +958,20 @@ def create_volume(self, volume):
903958
msg = _('Value %(v)s of %(k)s is invalid') % {'k': k, 'v': v}
904959
LOG.error(msg)
905960
raise exception.VolumeBackendAPIException(data=msg)
961+
limit_policy_id = None
962+
if qos_specs_id:
963+
limit_policy = self.client.get_limit_policy(qos_specs_id)
964+
if not limit_policy:
965+
limit_policy = self.client.create_limit_policy(
966+
qos_specs_id,
967+
max_iops=qos_specs.get(QOS_MAX_IOPS, None),
968+
max_kbps=qos_specs.get(QOS_MAX_BWS, None))
969+
else:
970+
limit_policy = limit_policy[0]
971+
limit_policy_id = limit_policy['id']
906972
err, lun = self.client.create_lun(
907973
self._get_target_storage_pool_id(volume), name, size,
908-
is_thin=is_thin)
974+
is_thin=is_thin, limit_policy_id=limit_policy_id)
909975
if err:
910976
raise exception.VolumeBackendAPIException(data=err['messages'])
911977

0 commit comments

Comments
 (0)