QoS support for Huawei Driver
QoS is a common attribute, so add the QoS capability in Huawei Driver. Partially implements: bp manila-support-qos Change-Id: I394f7d2ece96bf75edbea604e5db0f030f4d8970
This commit is contained in:
parent
29d5a10cab
commit
fb80e16126
@ -19,15 +19,20 @@ STATUS_FS_RUNNING = "27"
|
|||||||
STATUS_JOIN_DOMAIN = '1'
|
STATUS_JOIN_DOMAIN = '1'
|
||||||
STATUS_EXIT_DOMAIN = '0'
|
STATUS_EXIT_DOMAIN = '0'
|
||||||
STATUS_SERVICE_RUNNING = "2"
|
STATUS_SERVICE_RUNNING = "2"
|
||||||
|
STATUS_QOS_ACTIVE = '2'
|
||||||
|
|
||||||
DEFAULT_WAIT_INTERVAL = 3
|
DEFAULT_WAIT_INTERVAL = 3
|
||||||
DEFAULT_TIMEOUT = 60
|
DEFAULT_TIMEOUT = 60
|
||||||
|
|
||||||
|
MAX_FS_NUM_IN_QOS = 64
|
||||||
MSG_SNAPSHOT_NOT_FOUND = 1073754118
|
MSG_SNAPSHOT_NOT_FOUND = 1073754118
|
||||||
IP_ALLOCATIONS_DHSS_FALSE = 0
|
IP_ALLOCATIONS_DHSS_FALSE = 0
|
||||||
IP_ALLOCATIONS_DHSS_TRUE = 1
|
IP_ALLOCATIONS_DHSS_TRUE = 1
|
||||||
SOCKET_TIMEOUT = 52
|
SOCKET_TIMEOUT = 52
|
||||||
LOGIN_SOCKET_TIMEOUT = 4
|
LOGIN_SOCKET_TIMEOUT = 4
|
||||||
|
QOS_NAME_PREFIX = 'OpenStack_'
|
||||||
SYSTEM_NAME_PREFIX = "Array-"
|
SYSTEM_NAME_PREFIX = "Array-"
|
||||||
|
ARRAY_VERSION = 'V300R003C00'
|
||||||
|
|
||||||
ACCESS_NFS_RW = "1"
|
ACCESS_NFS_RW = "1"
|
||||||
ACCESS_NFS_RO = "0"
|
ACCESS_NFS_RO = "0"
|
||||||
@ -51,6 +56,14 @@ ALLOC_TYPE_THICK = "Thick"
|
|||||||
THIN_PROVISIONING = "true"
|
THIN_PROVISIONING = "true"
|
||||||
THICK_PROVISIONING = "false"
|
THICK_PROVISIONING = "false"
|
||||||
|
|
||||||
|
OPTS_QOS_VALUE = {
|
||||||
|
'maxiops': None,
|
||||||
|
'miniops': None,
|
||||||
|
'minbandwidth': None,
|
||||||
|
'maxbandwidth': None,
|
||||||
|
'latency': None,
|
||||||
|
'iotype': None
|
||||||
|
}
|
||||||
|
|
||||||
OPTS_CAPABILITIES = {
|
OPTS_CAPABILITIES = {
|
||||||
'dedupe': False,
|
'dedupe': False,
|
||||||
@ -58,6 +71,7 @@ OPTS_CAPABILITIES = {
|
|||||||
'huawei_smartcache': False,
|
'huawei_smartcache': False,
|
||||||
'huawei_smartpartition': False,
|
'huawei_smartpartition': False,
|
||||||
'thin_provisioning': False,
|
'thin_provisioning': False,
|
||||||
|
'qos': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
OPTS_VALUE = {
|
OPTS_VALUE = {
|
||||||
@ -65,7 +79,10 @@ OPTS_VALUE = {
|
|||||||
'partitionname': None,
|
'partitionname': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OPTS_VALUE.update(OPTS_QOS_VALUE)
|
||||||
|
|
||||||
OPTS_ASSOCIATE = {
|
OPTS_ASSOCIATE = {
|
||||||
'huawei_smartcache': 'cachename',
|
'huawei_smartcache': 'cachename',
|
||||||
'huawei_smartpartition': 'partitionname',
|
'huawei_smartpartition': 'partitionname',
|
||||||
|
'qos': OPTS_QOS_VALUE,
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ class HuaweiNasDriver(driver.ShareDriver):
|
|||||||
Support multi pools in one backend.
|
Support multi pools in one backend.
|
||||||
1.2 - Add share server support.
|
1.2 - Add share server support.
|
||||||
Add ensure share.
|
Add ensure share.
|
||||||
|
Add QoS support.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -175,6 +176,7 @@ class HuaweiNasDriver(driver.ShareDriver):
|
|||||||
vendor_name='Huawei',
|
vendor_name='Huawei',
|
||||||
driver_version='1.2',
|
driver_version='1.2',
|
||||||
storage_protocol='NFS_CIFS',
|
storage_protocol='NFS_CIFS',
|
||||||
|
qos=True,
|
||||||
total_capacity_gb=0.0,
|
total_capacity_gb=0.0,
|
||||||
free_capacity_gb=0.0)
|
free_capacity_gb=0.0)
|
||||||
|
|
||||||
|
@ -72,6 +72,6 @@ def _get_opts_from_specs(specs):
|
|||||||
if ((scope in constants.OPTS_CAPABILITIES) and
|
if ((scope in constants.OPTS_CAPABILITIES) and
|
||||||
(key in constants.OPTS_VALUE)):
|
(key in constants.OPTS_VALUE)):
|
||||||
if ((scope in constants.OPTS_ASSOCIATE) and
|
if ((scope in constants.OPTS_ASSOCIATE) and
|
||||||
(constants.OPTS_ASSOCIATE[scope] == key)):
|
(key in constants.OPTS_ASSOCIATE[scope])):
|
||||||
opts[key] = value
|
opts[key] = value
|
||||||
return opts
|
return opts
|
||||||
|
@ -95,6 +95,9 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
'running': fs['RUNNINGSTATUS']}))
|
'running': fs['RUNNINGSTATUS']}))
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if fs_id is not None:
|
if fs_id is not None:
|
||||||
|
qos_id = self.helper.get_qosid_by_fsid(fs_id)
|
||||||
|
if qos_id:
|
||||||
|
self.remove_qos_fs(fs_id, qos_id)
|
||||||
self.helper._delete_fs(fs_id)
|
self.helper._delete_fs(fs_id)
|
||||||
message = (_('Failed to create share %(name)s.'
|
message = (_('Failed to create share %(name)s.'
|
||||||
'Reason: %(err)s.')
|
'Reason: %(err)s.')
|
||||||
@ -106,6 +109,9 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
self.helper._create_share(share_name, fs_id, share_proto)
|
self.helper._create_share(share_name, fs_id, share_proto)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if fs_id is not None:
|
if fs_id is not None:
|
||||||
|
qos_id = self.helper.get_qosid_by_fsid(fs_id)
|
||||||
|
if qos_id:
|
||||||
|
self.remove_qos_fs(fs_id, qos_id)
|
||||||
self.helper._delete_fs(fs_id)
|
self.helper._delete_fs(fs_id)
|
||||||
raise exception.InvalidShare(
|
raise exception.InvalidShare(
|
||||||
reason=(_('Failed to create share %(name)s. Reason: %(err)s.')
|
reason=(_('Failed to create share %(name)s. Reason: %(err)s.')
|
||||||
@ -277,7 +283,7 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
self.configuration.safe_get(
|
self.configuration.safe_get(
|
||||||
'max_over_subscription_ratio')),
|
'max_over_subscription_ratio')),
|
||||||
allocated_capacity_gb=capacity['CONSUMEDCAPACITY'],
|
allocated_capacity_gb=capacity['CONSUMEDCAPACITY'],
|
||||||
qos=False,
|
qos=True,
|
||||||
reserved_percentage=0,
|
reserved_percentage=0,
|
||||||
thin_provisioning=[True, False],
|
thin_provisioning=[True, False],
|
||||||
dedupe=[True, False],
|
dedupe=[True, False],
|
||||||
@ -315,6 +321,9 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
self.helper._delete_share_by_id(share_id, share_url_type)
|
self.helper._delete_share_by_id(share_id, share_url_type)
|
||||||
|
|
||||||
if share_fs_id:
|
if share_fs_id:
|
||||||
|
qos_id = self.helper.get_qosid_by_fsid(share_fs_id)
|
||||||
|
if qos_id:
|
||||||
|
self.remove_qos_fs(share_fs_id, qos_id)
|
||||||
self.helper._delete_fs(share_fs_id)
|
self.helper._delete_fs(share_fs_id)
|
||||||
|
|
||||||
return share
|
return share
|
||||||
@ -498,12 +507,16 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
smartx_opts = constants.OPTS_CAPABILITIES
|
smartx_opts = constants.OPTS_CAPABILITIES
|
||||||
if opts is not None:
|
if opts is not None:
|
||||||
smart = smartx.SmartX()
|
smart = smartx.SmartX()
|
||||||
smartx_opts = smart.get_smartx_extra_specs_opts(opts)
|
smartx_opts, qos = smart.get_smartx_extra_specs_opts(opts)
|
||||||
|
|
||||||
fileParam = self._init_filesys_para(share, poolinfo, smartx_opts)
|
fileParam = self._init_filesys_para(share, poolinfo, smartx_opts)
|
||||||
fsid = self.helper._create_filesystem(fileParam)
|
fsid = self.helper._create_filesystem(fileParam)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if qos:
|
||||||
|
smart_qos = smartx.SmartQos(self.helper)
|
||||||
|
smart_qos.create_qos(qos, fsid)
|
||||||
|
|
||||||
smartpartition = smartx.SmartPartition(self.helper)
|
smartpartition = smartx.SmartPartition(self.helper)
|
||||||
smartpartition.add(opts, fsid)
|
smartpartition.add(opts, fsid)
|
||||||
|
|
||||||
@ -511,6 +524,9 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
smartcache.add(opts, fsid)
|
smartcache.add(opts, fsid)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if fsid is not None:
|
if fsid is not None:
|
||||||
|
qos_id = self.helper.get_qosid_by_fsid(fsid)
|
||||||
|
if qos_id:
|
||||||
|
self.remove_qos_fs(fsid, qos_id)
|
||||||
self.helper._delete_fs(fsid)
|
self.helper._delete_fs(fsid)
|
||||||
message = (_('Failed to add smartx. Reason: %(err)s.')
|
message = (_('Failed to add smartx. Reason: %(err)s.')
|
||||||
% {'err': err})
|
% {'err': err})
|
||||||
@ -651,7 +667,7 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
smartx_opts = constants.OPTS_CAPABILITIES
|
smartx_opts = constants.OPTS_CAPABILITIES
|
||||||
if opts is not None:
|
if opts is not None:
|
||||||
smart = smartx.SmartX()
|
smart = smartx.SmartX()
|
||||||
smartx_opts = smart.get_smartx_extra_specs_opts(opts)
|
smartx_opts, qos = smart.get_smartx_extra_specs_opts(opts)
|
||||||
|
|
||||||
old_compression = fs['COMPRESSION']
|
old_compression = fs['COMPRESSION']
|
||||||
new_compression = smartx_opts['compression']
|
new_compression = smartx_opts['compression']
|
||||||
@ -756,6 +772,17 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
"new_compression": new_compression})
|
"new_compression": new_compression})
|
||||||
LOG.info(msg)
|
LOG.info(msg)
|
||||||
|
|
||||||
|
def remove_qos_fs(self, fs_id, qos_id):
|
||||||
|
fs_list = self.helper.get_fs_list_in_qos(qos_id)
|
||||||
|
fs_count = len(fs_list)
|
||||||
|
if fs_count <= 1:
|
||||||
|
qos = smartx.SmartQos(self.helper)
|
||||||
|
qos.delete_qos(qos_id)
|
||||||
|
else:
|
||||||
|
self.helper.remove_fs_from_qos(fs_id,
|
||||||
|
fs_list,
|
||||||
|
qos_id)
|
||||||
|
|
||||||
def _get_location_path(self, share_name, share_proto, ip=None):
|
def _get_location_path(self, share_name, share_proto, ip=None):
|
||||||
location = None
|
location = None
|
||||||
if ip is None:
|
if ip is None:
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import time
|
||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
@ -811,6 +812,161 @@ class RestHelper(object):
|
|||||||
|
|
||||||
self._assert_rest_result(result, _('Add filesystem to cache error.'))
|
self._assert_rest_result(result, _('Add filesystem to cache error.'))
|
||||||
|
|
||||||
|
def get_qos(self):
|
||||||
|
url = "/ioclass"
|
||||||
|
result = self.call(url, None, "GET")
|
||||||
|
self._assert_rest_result(result, _('Get QoS information error.'))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def find_available_qos(self, qos):
|
||||||
|
""""Find available QoS on the array."""
|
||||||
|
qos_id = None
|
||||||
|
fs_list = []
|
||||||
|
result = self.get_qos()
|
||||||
|
|
||||||
|
if 'data' in result:
|
||||||
|
for item in result['data']:
|
||||||
|
qos_flag = 0
|
||||||
|
for key in qos:
|
||||||
|
if ((key not in item)
|
||||||
|
or qos[key] != item[key]
|
||||||
|
or int(item[key]) == 0):
|
||||||
|
break
|
||||||
|
qos_flag = qos_flag + 1
|
||||||
|
|
||||||
|
fs_num = len(item['FSLIST'].split(","))
|
||||||
|
# We use this QoS only if the filesystems in it is less
|
||||||
|
# than 64, else we cannot add filesystem to this QoS any more.
|
||||||
|
if (item['RUNNINGSTATUS'] == constants.STATUS_QOS_ACTIVE
|
||||||
|
and fs_num < constants.MAX_FS_NUM_IN_QOS
|
||||||
|
and constants.QOS_NAME_PREFIX in item['NAME']
|
||||||
|
and item['LUNLIST'] == '[""]'
|
||||||
|
and qos_flag == len(qos)):
|
||||||
|
qos_id = item['ID']
|
||||||
|
fs_list = item['FSLIST']
|
||||||
|
break
|
||||||
|
return (qos_id, fs_list)
|
||||||
|
|
||||||
|
def add_share_to_qos(self, qos_id, fs_id, fs_list):
|
||||||
|
"""Add filesystem to QoS."""
|
||||||
|
url = "/ioclass/" + qos_id
|
||||||
|
new_fs_list = []
|
||||||
|
fs_list_string = fs_list[1:-1]
|
||||||
|
for fs_string in fs_list_string.split(","):
|
||||||
|
tmp_fs_id = fs_string[1:-1]
|
||||||
|
if '' != tmp_fs_id and tmp_fs_id != fs_id:
|
||||||
|
new_fs_list.append(tmp_fs_id)
|
||||||
|
|
||||||
|
new_fs_list.append(fs_id)
|
||||||
|
|
||||||
|
data = jsonutils.dumps({"FSLIST": new_fs_list,
|
||||||
|
"TYPE": 230,
|
||||||
|
"ID": qos_id})
|
||||||
|
result = self.call(url, data, "PUT")
|
||||||
|
msg = _('Associate filesystem to Qos error.')
|
||||||
|
self._assert_rest_result(result, msg)
|
||||||
|
|
||||||
|
def create_qos_policy(self, qos, fs_id):
|
||||||
|
# Get local time.
|
||||||
|
localtime = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
|
||||||
|
# Package QoS name.
|
||||||
|
qos_name = constants.QOS_NAME_PREFIX + fs_id + '_' + localtime
|
||||||
|
|
||||||
|
mergedata = {
|
||||||
|
"TYPE": "230",
|
||||||
|
"NAME": qos_name,
|
||||||
|
"FSLIST": ["%s" % fs_id],
|
||||||
|
"CLASSTYPE": "1",
|
||||||
|
"SCHEDULEPOLICY": "2",
|
||||||
|
"SCHEDULESTARTTIME": "1410969600",
|
||||||
|
"STARTTIME": "08:00",
|
||||||
|
"DURATION": "86400",
|
||||||
|
"CYCLESET": "[1,2,3,4,5,6,0]",
|
||||||
|
}
|
||||||
|
mergedata.update(qos)
|
||||||
|
data = jsonutils.dumps(mergedata)
|
||||||
|
url = "/ioclass"
|
||||||
|
|
||||||
|
result = self.call(url, data)
|
||||||
|
self._assert_rest_result(result, _('Create QoS policy error.'))
|
||||||
|
|
||||||
|
return result['data']['ID']
|
||||||
|
|
||||||
|
def activate_deactivate_qos(self, qos_id, enablestatus):
|
||||||
|
"""Activate or deactivate QoS.
|
||||||
|
|
||||||
|
enablestatus: true (activate)
|
||||||
|
enablestatus: false (deactivate)
|
||||||
|
"""
|
||||||
|
url = "/ioclass/active/" + qos_id
|
||||||
|
data = jsonutils.dumps({
|
||||||
|
"TYPE": 230,
|
||||||
|
"ID": qos_id,
|
||||||
|
"ENABLESTATUS": enablestatus})
|
||||||
|
result = self.call(url, data, "PUT")
|
||||||
|
self._assert_rest_result(
|
||||||
|
result, _('Activate or deactivate QoS error.'))
|
||||||
|
|
||||||
|
def change_fs_priority_high(self, fs_id):
|
||||||
|
"""Change fs priority to high."""
|
||||||
|
url = "/filesystem/" + fs_id
|
||||||
|
data = jsonutils.dumps({"IOPRIORITY": "3"})
|
||||||
|
|
||||||
|
result = self.call(url, data, "PUT")
|
||||||
|
self._assert_rest_result(
|
||||||
|
result, _('Change filesystem priority error.'))
|
||||||
|
|
||||||
|
def delete_qos_policy(self, qos_id):
|
||||||
|
"""Delete a QoS policy."""
|
||||||
|
url = "/ioclass/" + qos_id
|
||||||
|
data = jsonutils.dumps({"TYPE": "230",
|
||||||
|
"ID": qos_id})
|
||||||
|
|
||||||
|
result = self.call(url, data, 'DELETE')
|
||||||
|
self._assert_rest_result(result, _('Delete QoS policy error.'))
|
||||||
|
|
||||||
|
def get_qosid_by_fsid(self, fs_id):
|
||||||
|
"""Get QoS id by fs id."""
|
||||||
|
url = "/filesystem/" + fs_id
|
||||||
|
result = self.call(url, None, "GET")
|
||||||
|
self._assert_rest_result(
|
||||||
|
result, _('Get QoS id by filesystem id error.'))
|
||||||
|
|
||||||
|
return result['data']['IOCLASSID']
|
||||||
|
|
||||||
|
def get_fs_list_in_qos(self, qos_id):
|
||||||
|
"""Get the filesystem list in QoS."""
|
||||||
|
qos_info = self.get_qos_info(qos_id)
|
||||||
|
|
||||||
|
fs_list = []
|
||||||
|
fs_string = qos_info['FSLIST'][1:-1]
|
||||||
|
|
||||||
|
for fs in fs_string.split(","):
|
||||||
|
fs_id = fs[1:-1]
|
||||||
|
fs_list.append(fs_id)
|
||||||
|
|
||||||
|
return fs_list
|
||||||
|
|
||||||
|
def get_qos_info(self, qos_id):
|
||||||
|
"""Get QoS information."""
|
||||||
|
url = "/ioclass/" + qos_id
|
||||||
|
result = self.call(url, None, "GET")
|
||||||
|
self._assert_rest_result(result, _('Get QoS information error.'))
|
||||||
|
|
||||||
|
return result['data']
|
||||||
|
|
||||||
|
def remove_fs_from_qos(self, fs_id, fs_list, qos_id):
|
||||||
|
"""Remove filesystem from QoS."""
|
||||||
|
fs_list = [i for i in fs_list if i != fs_id]
|
||||||
|
url = "/ioclass/" + qos_id
|
||||||
|
data = jsonutils.dumps({"FSLIST": fs_list,
|
||||||
|
"TYPE": 230,
|
||||||
|
"ID": qos_id})
|
||||||
|
result = self.call(url, data, "PUT")
|
||||||
|
|
||||||
|
msg = _('Remove filesystem from QoS error.')
|
||||||
|
self._assert_rest_result(result, msg)
|
||||||
|
|
||||||
def _remove_fs_from_cache(self, fs_id, cache_id):
|
def _remove_fs_from_cache(self, fs_id, cache_id):
|
||||||
url = "/SMARTCACHEPARTITION/REMOVE_ASSOCIATE"
|
url = "/SMARTCACHEPARTITION/REMOVE_ASSOCIATE"
|
||||||
data = jsonutils.dumps({"ID": cache_id,
|
data = jsonutils.dumps({"ID": cache_id,
|
||||||
@ -1094,3 +1250,9 @@ class RestHelper(object):
|
|||||||
return True, result['LDAPSERVER']
|
return True, result['LDAPSERVER']
|
||||||
|
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
|
def find_array_version(self):
|
||||||
|
url = "/system/"
|
||||||
|
result = self.call(url, None)
|
||||||
|
self._assert_rest_result(result, _('Find array version error.'))
|
||||||
|
return result['data']['PRODUCTVERSION']
|
||||||
|
@ -13,10 +13,12 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_utils import excutils
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
|
|
||||||
from manila import exception
|
from manila import exception
|
||||||
from manila.i18n import _
|
from manila.i18n import _
|
||||||
|
from manila.share.drivers.huawei import constants
|
||||||
|
|
||||||
|
|
||||||
class SmartPartition(object):
|
class SmartPartition(object):
|
||||||
@ -60,6 +62,49 @@ class SmartCache(object):
|
|||||||
self.helper._add_fs_to_cache(fsid, cache_id)
|
self.helper._add_fs_to_cache(fsid, cache_id)
|
||||||
|
|
||||||
|
|
||||||
|
class SmartQos(object):
|
||||||
|
def __init__(self, helper):
|
||||||
|
self.helper = helper
|
||||||
|
|
||||||
|
def create_qos(self, qos, fs_id):
|
||||||
|
policy_id = None
|
||||||
|
try:
|
||||||
|
# Check QoS priority.
|
||||||
|
if self._check_qos_high_priority(qos):
|
||||||
|
self.helper.change_fs_priority_high(fs_id)
|
||||||
|
# Create QoS policy and activate it.
|
||||||
|
version = self.helper.find_array_version()
|
||||||
|
if version >= constants.ARRAY_VERSION:
|
||||||
|
(qos_id, fs_list) = self.helper.find_available_qos(qos)
|
||||||
|
if qos_id is not None:
|
||||||
|
self.helper.add_share_to_qos(qos_id, fs_id, fs_list)
|
||||||
|
else:
|
||||||
|
policy_id = self.helper.create_qos_policy(qos, fs_id)
|
||||||
|
self.helper.activate_deactivate_qos(policy_id, True)
|
||||||
|
else:
|
||||||
|
policy_id = self.helper.create_qos_policy(qos, fs_id)
|
||||||
|
self.helper.activate_deactivate_qos(policy_id, True)
|
||||||
|
except exception.InvalidInput:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
if policy_id is not None:
|
||||||
|
self.helper.delete_qos_policy(policy_id)
|
||||||
|
|
||||||
|
def _check_qos_high_priority(self, qos):
|
||||||
|
"""Check QoS priority."""
|
||||||
|
for key, value in qos.items():
|
||||||
|
if (key.find('MIN') == 0) or (key.find('LATENCY') == 0):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_qos(self, qos_id):
|
||||||
|
qos_info = self.helper.get_qos_info(qos_id)
|
||||||
|
qos_status = qos_info['RUNNINGSTATUS']
|
||||||
|
if qos_status == constants.STATUS_QOS_ACTIVE:
|
||||||
|
self.helper.activate_deactivate_qos(qos_id, False)
|
||||||
|
self.helper.delete_qos_policy(qos_id)
|
||||||
|
|
||||||
|
|
||||||
class SmartX(object):
|
class SmartX(object):
|
||||||
def get_smartx_extra_specs_opts(self, opts):
|
def get_smartx_extra_specs_opts(self, opts):
|
||||||
opts = self.get_capabilities_opts(opts, 'dedupe')
|
opts = self.get_capabilities_opts(opts, 'dedupe')
|
||||||
@ -67,7 +112,8 @@ class SmartX(object):
|
|||||||
opts = self.get_smartprovisioning_opts(opts)
|
opts = self.get_smartprovisioning_opts(opts)
|
||||||
opts = self.get_smartcache_opts(opts)
|
opts = self.get_smartcache_opts(opts)
|
||||||
opts = self.get_smartpartition_opts(opts)
|
opts = self.get_smartpartition_opts(opts)
|
||||||
return opts
|
qos = self.get_qos_opts(opts)
|
||||||
|
return opts, qos
|
||||||
|
|
||||||
def get_capabilities_opts(self, opts, key):
|
def get_capabilities_opts(self, opts, key):
|
||||||
if strutils.bool_from_string(opts[key]):
|
if strutils.bool_from_string(opts[key]):
|
||||||
@ -106,3 +152,24 @@ class SmartX(object):
|
|||||||
opts['partitionname'] = None
|
opts['partitionname'] = None
|
||||||
|
|
||||||
return opts
|
return opts
|
||||||
|
|
||||||
|
def get_qos_opts(self, opts):
|
||||||
|
qos = {}
|
||||||
|
if not strutils.bool_from_string(opts.get('qos')):
|
||||||
|
return
|
||||||
|
|
||||||
|
for key, value in opts.items():
|
||||||
|
if (key in constants.OPTS_QOS_VALUE) and value is not None:
|
||||||
|
if (key.upper() != 'IOTYPE') and (int(value) <= 0):
|
||||||
|
err_msg = (_('QoS config is wrong. %(key)s'
|
||||||
|
' must be set greater than 0.')
|
||||||
|
% {'key': key})
|
||||||
|
raise exception.InvalidInput(reason=err_msg)
|
||||||
|
elif ((key.upper() == 'IOTYPE')
|
||||||
|
and (value not in ['0', '1', '2'])):
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=(_('Illegal value specified for IOTYPE: '
|
||||||
|
'set to either 0, 1, or 2.')))
|
||||||
|
else:
|
||||||
|
qos[key.upper()] = value
|
||||||
|
return qos
|
||||||
|
@ -81,6 +81,8 @@ def filesystem(method, data, fs_status_flag):
|
|||||||
data = """{"error":{"code":0},
|
data = """{"error":{"code":0},
|
||||||
"data":{"ID":"4",
|
"data":{"ID":"4",
|
||||||
"CAPACITY":"8388608"}}"""
|
"CAPACITY":"8388608"}}"""
|
||||||
|
elif data == """{"IOPRIORITY": "3"}""":
|
||||||
|
data = """{"error":{"code":0}}"""
|
||||||
elif method == "DELETE":
|
elif method == "DELETE":
|
||||||
data = """{"error":{"code":0}}"""
|
data = """{"error":{"code":0}}"""
|
||||||
elif method == "GET":
|
elif method == "GET":
|
||||||
@ -94,7 +96,8 @@ def filesystem(method, data, fs_status_flag):
|
|||||||
"ENABLECOMPRESSION":"false",
|
"ENABLECOMPRESSION":"false",
|
||||||
"ENABLEDEDUP":"false",
|
"ENABLEDEDUP":"false",
|
||||||
"CACHEPARTITIONID":"",
|
"CACHEPARTITIONID":"",
|
||||||
"SMARTCACHEPARTITIONID":""}}"""
|
"SMARTCACHEPARTITIONID":"",
|
||||||
|
"IOCLASSID":"11"}}"""
|
||||||
else:
|
else:
|
||||||
data = """{"error":{"code":0},
|
data = """{"error":{"code":0},
|
||||||
"data":{"HEALTHSTATUS":"0",
|
"data":{"HEALTHSTATUS":"0",
|
||||||
@ -105,7 +108,8 @@ def filesystem(method, data, fs_status_flag):
|
|||||||
"ENABLECOMPRESSION":"false",
|
"ENABLECOMPRESSION":"false",
|
||||||
"ENABLEDEDUP":"false",
|
"ENABLEDEDUP":"false",
|
||||||
"CACHEPARTITIONID":"",
|
"CACHEPARTITIONID":"",
|
||||||
"SMARTCACHEPARTITIONID":""}}"""
|
"SMARTCACHEPARTITIONID":"",
|
||||||
|
"IOCLASSID":"11"}}"""
|
||||||
else:
|
else:
|
||||||
data = '{"error":{"code":31755596}}'
|
data = '{"error":{"code":31755596}}'
|
||||||
return (data, extend_share_flag, shrink_share_flag)
|
return (data, extend_share_flag, shrink_share_flag)
|
||||||
@ -148,7 +152,8 @@ def filesystem_thick(method, data, fs_status_flag):
|
|||||||
"ENABLECOMPRESSION":"false",
|
"ENABLECOMPRESSION":"false",
|
||||||
"ENABLEDEDUP":"false",
|
"ENABLEDEDUP":"false",
|
||||||
"CACHEPARTITIONID":"",
|
"CACHEPARTITIONID":"",
|
||||||
"SMARTCACHEPARTITIONID":""}}"""
|
"SMARTCACHEPARTITIONID":"",
|
||||||
|
"IOCLASSID":"11"}}"""
|
||||||
else:
|
else:
|
||||||
data = """{"error":{"code":0},
|
data = """{"error":{"code":0},
|
||||||
"data":{"HEALTHSTATUS":"0",
|
"data":{"HEALTHSTATUS":"0",
|
||||||
@ -159,7 +164,8 @@ def filesystem_thick(method, data, fs_status_flag):
|
|||||||
"ENABLECOMPRESSION":"false",
|
"ENABLECOMPRESSION":"false",
|
||||||
"ENABLEDEDUP":"false",
|
"ENABLEDEDUP":"false",
|
||||||
"CACHEPARTITIONID":"",
|
"CACHEPARTITIONID":"",
|
||||||
"SMARTCACHEPARTITIONID":""}}"""
|
"SMARTCACHEPARTITIONID":"",
|
||||||
|
"IOCLASSID":"11"}}"""
|
||||||
else:
|
else:
|
||||||
data = '{"error":{"code":31755596}}'
|
data = '{"error":{"code":31755596}}'
|
||||||
return (data, extend_share_flag, shrink_share_flag)
|
return (data, extend_share_flag, shrink_share_flag)
|
||||||
@ -211,7 +217,8 @@ def filesystem_inpartition(method, data, fs_status_flag):
|
|||||||
"ENABLECOMPRESSION":"false",
|
"ENABLECOMPRESSION":"false",
|
||||||
"ENABLEDEDUP":"false",
|
"ENABLEDEDUP":"false",
|
||||||
"CACHEPARTITIONID":"1",
|
"CACHEPARTITIONID":"1",
|
||||||
"SMARTCACHEPARTITIONID":"1"}}"""
|
"SMARTCACHEPARTITIONID":"1",
|
||||||
|
"IOCLASSID":"11"}}"""
|
||||||
else:
|
else:
|
||||||
data = """{"error":{"code":0},
|
data = """{"error":{"code":0},
|
||||||
"data":{"HEALTHSTATUS":"0",
|
"data":{"HEALTHSTATUS":"0",
|
||||||
@ -222,7 +229,8 @@ def filesystem_inpartition(method, data, fs_status_flag):
|
|||||||
"ENABLECOMPRESSION":"false",
|
"ENABLECOMPRESSION":"false",
|
||||||
"ENABLEDEDUP":"false",
|
"ENABLEDEDUP":"false",
|
||||||
"CACHEPARTITIONID":"1",
|
"CACHEPARTITIONID":"1",
|
||||||
"SMARTCACHEPARTITIONID":"1"}}"""
|
"SMARTCACHEPARTITIONID":"1",
|
||||||
|
"IOCLASSID":"11"}}"""
|
||||||
else:
|
else:
|
||||||
data = '{"error":{"code":31755596}}'
|
data = '{"error":{"code":31755596}}'
|
||||||
return (data, extend_share_flag, shrink_share_flag)
|
return (data, extend_share_flag, shrink_share_flag)
|
||||||
@ -272,6 +280,19 @@ def dec_driver_handles_share_servers(func):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def QoS_response(method):
|
||||||
|
if method == "GET":
|
||||||
|
data = """{"error":{"code":0},
|
||||||
|
"data":[{"NAME": "OpenStack_Fake_QoS", "MAXIOPS": "100",
|
||||||
|
"FSLIST": 4, "LUNLIST": ""}]}"""
|
||||||
|
elif method == "PUT":
|
||||||
|
data = """{"error":{"code":0}}"""
|
||||||
|
else:
|
||||||
|
data = """{"error":{"code":0},
|
||||||
|
"data":{"ID": "11"}}"""
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class FakeHuaweiNasHelper(helper.RestHelper):
|
class FakeHuaweiNasHelper(helper.RestHelper):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -336,12 +357,30 @@ class FakeHuaweiNasHelper(helper.RestHelper):
|
|||||||
"NAME":"OpenStack_Pool",
|
"NAME":"OpenStack_Pool",
|
||||||
"USERTOTALCAPACITY":"4194304",
|
"USERTOTALCAPACITY":"4194304",
|
||||||
"USAGETYPE":"2",
|
"USAGETYPE":"2",
|
||||||
|
"USERCONSUMEDCAPACITY":"2097152"},
|
||||||
|
{"USERFREECAPACITY":"2097152",
|
||||||
|
"ID":"2",
|
||||||
|
"NAME":"OpenStack_Pool_Thick",
|
||||||
|
"USERTOTALCAPACITY":"4194304",
|
||||||
|
"USAGETYPE":"2",
|
||||||
"USERCONSUMEDCAPACITY":"2097152"}]}"""
|
"USERCONSUMEDCAPACITY":"2097152"}]}"""
|
||||||
|
|
||||||
if url == "/filesystem":
|
if url == "/filesystem":
|
||||||
data = """{"error":{"code":0},"data":{
|
data = """{"error":{"code":0},"data":{
|
||||||
"ID":"4"}}"""
|
"ID":"4"}}"""
|
||||||
|
|
||||||
|
if url == "/system/":
|
||||||
|
data = """{"error":{"code":0},
|
||||||
|
"data":{"PRODUCTVERSION": "V300R003C10"}}"""
|
||||||
|
|
||||||
|
if url == "/ioclass" or url == "/ioclass/11":
|
||||||
|
data = QoS_response(method)
|
||||||
|
|
||||||
|
if url == "/ioclass/active/11":
|
||||||
|
data = """{"error":{"code":0},
|
||||||
|
"data":[{"ID": "11", "MAXIOPS": "100",
|
||||||
|
"FSLIST": ""}]}"""
|
||||||
|
|
||||||
if url == "/NFSHARE" or url == "/CIFSHARE":
|
if url == "/NFSHARE" or url == "/CIFSHARE":
|
||||||
if self.create_share_flag:
|
if self.create_share_flag:
|
||||||
data = '{"error":{"code":31755596}}'
|
data = '{"error":{"code":31755596}}'
|
||||||
@ -1358,6 +1397,106 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
'share_type_get',
|
'share_type_get',
|
||||||
mock.Mock(return_value=share_type))
|
mock.Mock(return_value=share_type))
|
||||||
self.driver.plugin.helper.login()
|
self.driver.plugin.helper.login()
|
||||||
|
self.assertRaises(exception.InvalidShare,
|
||||||
|
self.driver.create_share,
|
||||||
|
self._context,
|
||||||
|
self.share_nfs_thick,
|
||||||
|
self.share_server)
|
||||||
|
|
||||||
|
@ddt.data({"fake_extra_specs_qos": {"qos:maxIOPS": "100",
|
||||||
|
"qos:minIOPS": "50"},
|
||||||
|
"fake_qos_info": {"MAXIOPS": "100", "MINIOPS": "50",
|
||||||
|
"NAME": "OpenStack_fake_qos"}},
|
||||||
|
{"fake_extra_specs_qos": {"qos:maxIOPS": "100",
|
||||||
|
"qos:minIOPS": "50"},
|
||||||
|
"fake_qos_info": {"NAME": "fake_qos", "MAXIOPS": "100"}})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_create_share_with_qos(self, fake_extra_specs_qos, fake_qos_info):
|
||||||
|
fake_share_type_id = 'fooid-2'
|
||||||
|
fake_extra_specs = {"capabilities:qos": "<is> True"}
|
||||||
|
fake_extra_specs.update(fake_extra_specs_qos)
|
||||||
|
|
||||||
|
fake_type_error_extra = {
|
||||||
|
'test_with_extra': {
|
||||||
|
'created_at': 'fake_time',
|
||||||
|
'deleted': '0',
|
||||||
|
'deleted_at': None,
|
||||||
|
'extra_specs': fake_extra_specs,
|
||||||
|
'required_extra_specs': {},
|
||||||
|
'id': fake_share_type_id,
|
||||||
|
'name': 'test_with_extra',
|
||||||
|
'updated_at': None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fake_qos_info_respons = {
|
||||||
|
"error": {
|
||||||
|
"code": 0
|
||||||
|
},
|
||||||
|
"data": [{
|
||||||
|
"ID": "11",
|
||||||
|
"FSLIST": u'["1", "2", "3", "4"]',
|
||||||
|
"LUNLIST": '[""]',
|
||||||
|
"RUNNINGSTATUS": "2",
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fake_qos_info_respons["data"][0].update(fake_qos_info)
|
||||||
|
share_type = fake_type_error_extra['test_with_extra']
|
||||||
|
self.mock_object(db,
|
||||||
|
'share_type_get',
|
||||||
|
mock.Mock(return_value=share_type))
|
||||||
|
self.mock_object(helper.RestHelper,
|
||||||
|
'get_qos',
|
||||||
|
mock.Mock(return_value=fake_qos_info_respons))
|
||||||
|
|
||||||
|
self.driver.plugin.configuration.manila_huawei_conf_file = (
|
||||||
|
self.fake_conf_file)
|
||||||
|
self.driver.plugin.helper.login()
|
||||||
|
|
||||||
|
location = self.driver.create_share(self._context, self.share_nfs,
|
||||||
|
self.share_server)
|
||||||
|
self.assertEqual("100.115.10.68:/share_fake_uuid", location)
|
||||||
|
|
||||||
|
@ddt.data({'capabilities:qos': '<is> True',
|
||||||
|
'qos:maxIOPS': -1},
|
||||||
|
{'capabilities:qos': '<is> True',
|
||||||
|
'qos:IOTYPE': 4},
|
||||||
|
{'capabilities:qos': '<is> True',
|
||||||
|
'qos:IOTYPE': 100},
|
||||||
|
{'capabilities:qos': '<is> True',
|
||||||
|
'qos:maxIOPS': 0},
|
||||||
|
{'capabilities:qos': '<is> True',
|
||||||
|
'qos:minIOPS': 0},
|
||||||
|
{'capabilities:qos': '<is> True',
|
||||||
|
'qos:minBandWidth': 0},
|
||||||
|
{'capabilities:qos': '<is> True',
|
||||||
|
'qos:maxBandWidth': 0},
|
||||||
|
{'capabilities:qos': '<is> True',
|
||||||
|
'qos:latency': 0})
|
||||||
|
def test_create_share_with_invalid_qos(self, fake_extra_specs):
|
||||||
|
fake_share_type_id = 'fooid-2'
|
||||||
|
fake_type_error_extra = {
|
||||||
|
'test_with_extra': {
|
||||||
|
'created_at': 'fake_time',
|
||||||
|
'deleted': '0',
|
||||||
|
'deleted_at': None,
|
||||||
|
'extra_specs': fake_extra_specs,
|
||||||
|
'required_extra_specs': {},
|
||||||
|
'id': fake_share_type_id,
|
||||||
|
'name': 'test_with_extra',
|
||||||
|
'updated_at': None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
share_type = fake_type_error_extra['test_with_extra']
|
||||||
|
self.mock_object(db,
|
||||||
|
'share_type_get',
|
||||||
|
mock.Mock(return_value=share_type))
|
||||||
|
|
||||||
|
self.driver.plugin.configuration.manila_huawei_conf_file = (
|
||||||
|
self.fake_conf_file)
|
||||||
|
self.driver.plugin.helper.login()
|
||||||
self.assertRaises(exception.InvalidShare,
|
self.assertRaises(exception.InvalidShare,
|
||||||
self.driver.create_share,
|
self.driver.create_share,
|
||||||
self._context,
|
self._context,
|
||||||
@ -1460,12 +1599,27 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
self.share_nfs,
|
self.share_nfs,
|
||||||
self.share_server)
|
self.share_server)
|
||||||
|
|
||||||
def test_delete_share_nfs_success(self):
|
@ddt.data({"share_proto": "NFS",
|
||||||
|
"fake_qos_info_respons": {"ID": "11", "MAXIOPS": "100",
|
||||||
|
"IOType": "2",
|
||||||
|
"FSLIST": u'["0", "1", "4"]'}},
|
||||||
|
{"share_proto": "CIFS",
|
||||||
|
"fake_qos_info_respons": {"ID": "11", "MAXIOPS": "100",
|
||||||
|
"IOType": "2", "FSLIST": u'["4"]',
|
||||||
|
"RUNNINGSTATUS": "2"}})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_delete_share_success(self, share_proto, fake_qos_info_respons):
|
||||||
self.driver.plugin.helper.login()
|
self.driver.plugin.helper.login()
|
||||||
self.driver.plugin.helper.delete_flag = False
|
self.driver.plugin.helper.delete_flag = False
|
||||||
self.driver.delete_share(self._context,
|
if share_proto == 'NFS':
|
||||||
self.share_nfs, self.share_server)
|
share = self.share_nfs
|
||||||
self.assertTrue(self.driver.plugin.helper.delete_flag)
|
else:
|
||||||
|
share = self.share_cifs
|
||||||
|
with mock.patch.object(helper.RestHelper, 'get_qos_info',
|
||||||
|
return_value=fake_qos_info_respons):
|
||||||
|
self.driver.delete_share(self._context,
|
||||||
|
share, self.share_server)
|
||||||
|
self.assertTrue(self.driver.plugin.helper.delete_flag)
|
||||||
|
|
||||||
def test_check_snapshot_id_exist_fail(self):
|
def test_check_snapshot_id_exist_fail(self):
|
||||||
snapshot_id = "4"
|
snapshot_id = "4"
|
||||||
@ -1484,8 +1638,19 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
self.assertTrue(self.driver.plugin.helper.delete_flag)
|
self.assertTrue(self.driver.plugin.helper.delete_flag)
|
||||||
|
|
||||||
def test_delete_share_cifs_success(self):
|
def test_delete_share_cifs_success(self):
|
||||||
self.driver.plugin.helper.login()
|
|
||||||
self.driver.plugin.helper.delete_flag = False
|
self.driver.plugin.helper.delete_flag = False
|
||||||
|
|
||||||
|
fake_qos_info_respons = {
|
||||||
|
"ID": "11",
|
||||||
|
"FSLIST": u'["1", "2", "3", "4"]',
|
||||||
|
"LUNLIST": '[""]',
|
||||||
|
"RUNNINGSTATUS": "2",
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mock_object(helper.RestHelper,
|
||||||
|
'get_qos_info',
|
||||||
|
mock.Mock(return_value=fake_qos_info_respons))
|
||||||
|
self.driver.plugin.helper.login()
|
||||||
self.driver.delete_share(self._context, self.share_cifs,
|
self.driver.delete_share(self._context, self.share_cifs,
|
||||||
self.share_server)
|
self.share_server)
|
||||||
self.assertTrue(self.driver.plugin.helper.delete_flag)
|
self.assertTrue(self.driver.plugin.helper.delete_flag)
|
||||||
@ -1527,7 +1692,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
expected['reserved_percentage'] = 0
|
expected['reserved_percentage'] = 0
|
||||||
expected['total_capacity_gb'] = 0.0
|
expected['total_capacity_gb'] = 0.0
|
||||||
expected['free_capacity_gb'] = 0.0
|
expected['free_capacity_gb'] = 0.0
|
||||||
expected['qos'] = False
|
expected['qos'] = True
|
||||||
expected["snapshot_support"] = False
|
expected["snapshot_support"] = False
|
||||||
expected["pools"] = []
|
expected["pools"] = []
|
||||||
pool = dict(
|
pool = dict(
|
||||||
@ -1535,7 +1700,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
total_capacity_gb=2.0,
|
total_capacity_gb=2.0,
|
||||||
free_capacity_gb=1.0,
|
free_capacity_gb=1.0,
|
||||||
allocated_capacity_gb=1.0,
|
allocated_capacity_gb=1.0,
|
||||||
qos=False,
|
qos=True,
|
||||||
reserved_percentage=0,
|
reserved_percentage=0,
|
||||||
compression=[True, False],
|
compression=[True, False],
|
||||||
dedupe=[True, False],
|
dedupe=[True, False],
|
||||||
|
Loading…
Reference in New Issue
Block a user