Merge "Add SmartX support for Huawei driver"
This commit is contained in:
commit
e1e33286ce
@ -30,6 +30,7 @@ from cinder.volume.drivers.huawei import constants
|
|||||||
from cinder.volume.drivers.huawei import huawei_driver
|
from cinder.volume.drivers.huawei import huawei_driver
|
||||||
from cinder.volume.drivers.huawei import huawei_utils
|
from cinder.volume.drivers.huawei import huawei_utils
|
||||||
from cinder.volume.drivers.huawei import rest_client
|
from cinder.volume.drivers.huawei import rest_client
|
||||||
|
from cinder.volume.drivers.huawei import smartx
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ test_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
|
|||||||
'display_name': 'vol1',
|
'display_name': 'vol1',
|
||||||
'display_description': 'test volume',
|
'display_description': 'test volume',
|
||||||
'volume_type_id': None,
|
'volume_type_id': None,
|
||||||
'host': 'ubuntu@huawei#OpenStack_Pool',
|
'host': 'ubuntu001@backend001#OpenStack_Pool',
|
||||||
'provider_location': '11',
|
'provider_location': '11',
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,12 +81,23 @@ FakeConnector = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
|
|||||||
'host': 'ubuntuc',
|
'host': 'ubuntuc',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smarttier_opts = {'smarttier': 'true',
|
||||||
|
'smartpartition': False,
|
||||||
|
'smartcache': False,
|
||||||
|
'thin_provisioning_support': True,
|
||||||
|
'thick_provisioning_support': False,
|
||||||
|
'policy': '3',
|
||||||
|
'readcachepolicy': '1',
|
||||||
|
'writecachepolicy': None,
|
||||||
|
}
|
||||||
|
|
||||||
# A fake response of success response storage
|
# A fake response of success response storage
|
||||||
FAKE_COMMON_SUCCESS_RESPONSE = """
|
FAKE_COMMON_SUCCESS_RESPONSE = """
|
||||||
{
|
{
|
||||||
"error": {
|
"error": {
|
||||||
"code": 0
|
"code": 0
|
||||||
}
|
},
|
||||||
|
"data":{}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -154,7 +166,8 @@ FAKE_LUN_DELETE_SUCCESS_RESPONSE = """
|
|||||||
"NAME": "5mFHcBv4RkCcD+JyrWc0SA",
|
"NAME": "5mFHcBv4RkCcD+JyrWc0SA",
|
||||||
"RUNNINGSTATUS": "2",
|
"RUNNINGSTATUS": "2",
|
||||||
"HEALTHSTATUS": "1",
|
"HEALTHSTATUS": "1",
|
||||||
"RUNNINGSTATUS": "27"
|
"RUNNINGSTATUS": "27",
|
||||||
|
"LUNLIST": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@ -312,7 +325,7 @@ FAKE_GET_ETH_INFO_RESPONSE = """
|
|||||||
"MACADDRESS": "00:22:a1:0a:79:57",
|
"MACADDRESS": "00:22:a1:0a:79:57",
|
||||||
"ETHNEGOTIATE": "-1",
|
"ETHNEGOTIATE": "-1",
|
||||||
"ERRORPACKETS": "0",
|
"ERRORPACKETS": "0",
|
||||||
"IPV4ADDR": "198.100.10.1",
|
"IPV4ADDR": "192.168.1.2",
|
||||||
"IPV6GATEWAY": "",
|
"IPV6GATEWAY": "",
|
||||||
"IPV6MASK": "0",
|
"IPV6MASK": "0",
|
||||||
"OVERFLOWEDPACKETS": "0",
|
"OVERFLOWEDPACKETS": "0",
|
||||||
@ -342,7 +355,7 @@ FAKE_GET_ETH_INFO_RESPONSE = """
|
|||||||
"MACADDRESS": "00:22:a1:0a:79:57",
|
"MACADDRESS": "00:22:a1:0a:79:57",
|
||||||
"ETHNEGOTIATE": "-1",
|
"ETHNEGOTIATE": "-1",
|
||||||
"ERRORPACKETS": "0",
|
"ERRORPACKETS": "0",
|
||||||
"IPV4ADDR": "198.100.10.2",
|
"IPV4ADDR": "192.168.1.1",
|
||||||
"IPV6GATEWAY": "",
|
"IPV6GATEWAY": "",
|
||||||
"IPV6MASK": "0",
|
"IPV6MASK": "0",
|
||||||
"OVERFLOWEDPACKETS": "0",
|
"OVERFLOWEDPACKETS": "0",
|
||||||
@ -376,12 +389,12 @@ FAKE_GET_ETH_ASSOCIATE_RESPONSE = """
|
|||||||
"code":0
|
"code":0
|
||||||
},
|
},
|
||||||
"data":[{
|
"data":[{
|
||||||
"IPV4ADDR": "198.100.10.1",
|
"IPV4ADDR": "192.168.1.1",
|
||||||
"HEALTHSTATUS": "1",
|
"HEALTHSTATUS": "1",
|
||||||
"RUNNINGSTATUS": "10"
|
"RUNNINGSTATUS": "10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IPV4ADDR": "198.100.10.2",
|
"IPV4ADDR": "192.168.1.2",
|
||||||
"HEALTHSTATUS": "1",
|
"HEALTHSTATUS": "1",
|
||||||
"RUNNINGSTATUS": "10"
|
"RUNNINGSTATUS": "10"
|
||||||
}
|
}
|
||||||
@ -448,7 +461,7 @@ FAKE_GET_ALL_HOST_GROUP_INFO_RESPONSE = """
|
|||||||
"code": 0
|
"code": 0
|
||||||
},
|
},
|
||||||
"data": [{
|
"data": [{
|
||||||
"NAME":"OpenStack_HostGroup_1",
|
"NAME": "OpenStack_HostGroup_1",
|
||||||
"DESCRIPTION":"",
|
"DESCRIPTION":"",
|
||||||
"ID":"0",
|
"ID":"0",
|
||||||
"TYPE":14
|
"TYPE":14
|
||||||
@ -645,7 +658,7 @@ FAKE_PORT_GROUP_RESPONSE = """
|
|||||||
},
|
},
|
||||||
"data":[{
|
"data":[{
|
||||||
"ID":11,
|
"ID":11,
|
||||||
"NAME":"portgroup-test"
|
"NAME": "portgroup-test"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@ -653,19 +666,19 @@ FAKE_PORT_GROUP_RESPONSE = """
|
|||||||
FAKE_ISCSI_INITIATOR_RESPONSE = """
|
FAKE_ISCSI_INITIATOR_RESPONSE = """
|
||||||
{
|
{
|
||||||
"error":{
|
"error":{
|
||||||
"code":0
|
"code": 0
|
||||||
},
|
},
|
||||||
"data":[{
|
"data":[{
|
||||||
"CHAPNAME":"mm-user",
|
"CHAPNAME": "mm-user",
|
||||||
"HEALTHSTATUS":"1",
|
"HEALTHSTATUS": "1",
|
||||||
"ID":"iqn.1993-08.org.debian:01:9073aba6c6f",
|
"ID": "iqn.1993-08.org.debian:01:9073aba6c6f",
|
||||||
"ISFREE":"true",
|
"ISFREE": "true",
|
||||||
"MULTIPATHTYPE":"1",
|
"MULTIPATHTYPE": "1",
|
||||||
"NAME":"",
|
"NAME": "",
|
||||||
"OPERATIONSYSTEM":"255",
|
"OPERATIONSYSTEM": "255",
|
||||||
"RUNNINGSTATUS":"28",
|
"RUNNINGSTATUS": "28",
|
||||||
"TYPE":222,
|
"TYPE": 222,
|
||||||
"USECHAP":"true"
|
"USECHAP": "true"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@ -710,6 +723,29 @@ FAKE_ERROR_LUN_INFO_RESPONSE = """
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
FAKE_SYSTEM_VERSION_RESPONSE = """
|
||||||
|
{
|
||||||
|
"error":{
|
||||||
|
"code": 0
|
||||||
|
},
|
||||||
|
"data":{
|
||||||
|
"PRODUCTVERSION": "V100R001C10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
FAKE_QOS_INFO_RESPONSE = """
|
||||||
|
{
|
||||||
|
"error":{
|
||||||
|
"code": 0
|
||||||
|
},
|
||||||
|
"data":{
|
||||||
|
"ID": "11"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
# mock login info map
|
# mock login info map
|
||||||
MAP_COMMAND_TO_FAKE_RESPONSE = {}
|
MAP_COMMAND_TO_FAKE_RESPONSE = {}
|
||||||
MAP_COMMAND_TO_FAKE_RESPONSE['/xx/sessions'] = (
|
MAP_COMMAND_TO_FAKE_RESPONSE['/xx/sessions'] = (
|
||||||
@ -734,6 +770,9 @@ MAP_COMMAND_TO_FAKE_RESPONSE['lun/1/GET'] = (
|
|||||||
MAP_COMMAND_TO_FAKE_RESPONSE['lun/11/DELETE'] = (
|
MAP_COMMAND_TO_FAKE_RESPONSE['lun/11/DELETE'] = (
|
||||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||||
|
|
||||||
|
MAP_COMMAND_TO_FAKE_RESPONSE['lun/1/DELETE'] = (
|
||||||
|
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||||
|
|
||||||
MAP_COMMAND_TO_FAKE_RESPONSE['lun?range=[0-65535]/GET'] = (
|
MAP_COMMAND_TO_FAKE_RESPONSE['lun?range=[0-65535]/GET'] = (
|
||||||
FAKE_QUERY_ALL_LUN_RESPONSE)
|
FAKE_QUERY_ALL_LUN_RESPONSE)
|
||||||
|
|
||||||
@ -770,6 +809,10 @@ MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?TYPE=256&ASSOCIATEOBJTYPE=11'
|
|||||||
'&ASSOCIATEOBJID=11/GET'] = (
|
'&ASSOCIATEOBJID=11/GET'] = (
|
||||||
FAKE_LUN_ASSOCIATE_RESPONSE)
|
FAKE_LUN_ASSOCIATE_RESPONSE)
|
||||||
|
|
||||||
|
MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?TYPE=256&ASSOCIATEOBJTYPE=11'
|
||||||
|
'&ASSOCIATEOBJID=1/GET'] = (
|
||||||
|
FAKE_LUN_ASSOCIATE_RESPONSE)
|
||||||
|
|
||||||
MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?ID=11&ASSOCIATEOBJTYPE=11'
|
MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?ID=11&ASSOCIATEOBJTYPE=11'
|
||||||
'&ASSOCIATEOBJID=11/DELETE'] = (
|
'&ASSOCIATEOBJID=11/DELETE'] = (
|
||||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||||
@ -814,6 +857,9 @@ MAP_COMMAND_TO_FAKE_RESPONSE['ioclass/11/DELETE'] = (
|
|||||||
MAP_COMMAND_TO_FAKE_RESPONSE['ioclass/active/11/PUT'] = (
|
MAP_COMMAND_TO_FAKE_RESPONSE['ioclass/active/11/PUT'] = (
|
||||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||||
|
|
||||||
|
MAP_COMMAND_TO_FAKE_RESPONSE['ioclass/'] = (
|
||||||
|
FAKE_QOS_INFO_RESPONSE)
|
||||||
|
|
||||||
# mock iscsi info map
|
# mock iscsi info map
|
||||||
MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_tgt_port/GET'] = (
|
MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_tgt_port/GET'] = (
|
||||||
FAKE_GET_ISCSI_INFO_RESPONSE)
|
FAKE_GET_ISCSI_INFO_RESPONSE)
|
||||||
@ -896,6 +942,10 @@ MAP_COMMAND_TO_FAKE_RESPONSE['host_link?INITIATOR_TYPE=223'
|
|||||||
MAP_COMMAND_TO_FAKE_RESPONSE['portgroup?range=[0-8191]&TYPE=257/GET'] = (
|
MAP_COMMAND_TO_FAKE_RESPONSE['portgroup?range=[0-8191]&TYPE=257/GET'] = (
|
||||||
FAKE_PORT_GROUP_RESPONSE)
|
FAKE_PORT_GROUP_RESPONSE)
|
||||||
|
|
||||||
|
# mock system info map
|
||||||
|
MAP_COMMAND_TO_FAKE_RESPONSE['system/'] = (
|
||||||
|
FAKE_SYSTEM_VERSION_RESPONSE)
|
||||||
|
|
||||||
|
|
||||||
def Fake_sleep(time):
|
def Fake_sleep(time):
|
||||||
pass
|
pass
|
||||||
@ -941,6 +991,18 @@ class Fake18000Client(rest_client.RestClient):
|
|||||||
def _check_snapshot_exist(self, snapshot_id):
|
def _check_snapshot_exist(self, snapshot_id):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_partition_id_by_name(self, name):
|
||||||
|
return "11"
|
||||||
|
|
||||||
|
def add_lun_to_partition(self, lunid, partition_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_cache_id_by_name(self, name):
|
||||||
|
return "11"
|
||||||
|
|
||||||
|
def add_lun_to_cache(self, lunid, cache_id):
|
||||||
|
pass
|
||||||
|
|
||||||
def call(self, url=False, data=None, method=None):
|
def call(self, url=False, data=None, method=None):
|
||||||
url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '')
|
url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '')
|
||||||
command = url.replace('/210235G7J20000000000/', '')
|
command = url.replace('/210235G7J20000000000/', '')
|
||||||
@ -1002,11 +1064,11 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase):
|
|||||||
self.driver.do_setup()
|
self.driver.do_setup()
|
||||||
self.portgroup = 'portgroup-test'
|
self.portgroup = 'portgroup-test'
|
||||||
self.iscsi_iqns = ['iqn.2006-08.com.huawei:oceanstor:21000022a:'
|
self.iscsi_iqns = ['iqn.2006-08.com.huawei:oceanstor:21000022a:'
|
||||||
':20500:198.100.10.1',
|
':20503:192.168.1.1',
|
||||||
'iqn.2006-08.com.huawei:oceanstor:21000022a:'
|
'iqn.2006-08.com.huawei:oceanstor:21000022a:'
|
||||||
':20503:198.100.10.2']
|
':20500:192.168.1.2']
|
||||||
self.target_ips = ['198.100.10.1',
|
self.target_ips = ['192.168.1.1',
|
||||||
'198.100.10.2']
|
'192.168.1.2']
|
||||||
self.portgroup_id = 11
|
self.portgroup_id = 11
|
||||||
|
|
||||||
def test_login_success(self):
|
def test_login_success(self):
|
||||||
@ -1128,8 +1190,8 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase):
|
|||||||
def test_get_tgtip(self):
|
def test_get_tgtip(self):
|
||||||
self.driver.restclient.login()
|
self.driver.restclient.login()
|
||||||
portg_id = self.driver.restclient.find_tgt_port_group(self.portgroup)
|
portg_id = self.driver.restclient.find_tgt_port_group(self.portgroup)
|
||||||
result = self.driver.restclient._get_tgt_ip_from_portgroup(portg_id)
|
target_ip = self.driver.restclient._get_tgt_ip_from_portgroup(portg_id)
|
||||||
self.assertEqual(self.target_ips, result)
|
self.assertEqual(self.target_ips, target_ip)
|
||||||
|
|
||||||
def test_get_iscsi_params(self):
|
def test_get_iscsi_params(self):
|
||||||
self.driver.restclient.login()
|
self.driver.restclient.login()
|
||||||
@ -1226,6 +1288,33 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase):
|
|||||||
pool_info = self.driver.restclient.find_pool_info(pool_name, pools)
|
pool_info = self.driver.restclient.find_pool_info(pool_name, pools)
|
||||||
self.assertEqual(test_info, pool_info)
|
self.assertEqual(test_info, pool_info)
|
||||||
|
|
||||||
|
def test_get_smartx_specs_opts(self):
|
||||||
|
self.driver.restclient.login()
|
||||||
|
smartx_opts = smartx.SmartX().get_smartx_specs_opts(smarttier_opts)
|
||||||
|
self.assertEqual('3', smartx_opts['policy'])
|
||||||
|
|
||||||
|
@mock.patch.object(huawei_utils, 'get_volume_qos',
|
||||||
|
return_value={'MAXIOPS': '100',
|
||||||
|
'IOType': '2'})
|
||||||
|
def test_create_smartqos(self, mock_qos_value):
|
||||||
|
self.driver.restclient.login()
|
||||||
|
lun_info = self.driver.create_volume(test_volume)
|
||||||
|
self.assertEqual('1', lun_info['provider_location'])
|
||||||
|
|
||||||
|
@mock.patch.object(huawei_utils, 'get_volume_params',
|
||||||
|
return_value={'smarttier': 'true',
|
||||||
|
'smartcache': 'true',
|
||||||
|
'smartpartition': 'true',
|
||||||
|
'thin_provisioning_support': 'true',
|
||||||
|
'thick_provisioning_support': False,
|
||||||
|
'policy': '2',
|
||||||
|
'cachename': 'cache-test',
|
||||||
|
'partitionname': 'partition-test'})
|
||||||
|
def test_creat_smartx(self, mock_volume_types):
|
||||||
|
self.driver.restclient.login()
|
||||||
|
lun_info = self.driver.create_volume(test_volume)
|
||||||
|
self.assertEqual('1', lun_info['provider_location'])
|
||||||
|
|
||||||
def create_fake_conf_file(self):
|
def create_fake_conf_file(self):
|
||||||
"""Create a fake Config file.
|
"""Create a fake Config file.
|
||||||
|
|
||||||
@ -1297,7 +1386,7 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase):
|
|||||||
iscsi.appendChild(defaulttargetip)
|
iscsi.appendChild(defaulttargetip)
|
||||||
initiator = doc.createElement('Initiator')
|
initiator = doc.createElement('Initiator')
|
||||||
initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
|
initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
|
||||||
initiator.setAttribute('TargetIP', '192.168.100.2')
|
initiator.setAttribute('TargetIP', '192.168.1.2')
|
||||||
initiator.setAttribute('CHAPinfo', 'mm-user;mm-user@storage')
|
initiator.setAttribute('CHAPinfo', 'mm-user;mm-user@storage')
|
||||||
initiator.setAttribute('ALUA', '1')
|
initiator.setAttribute('ALUA', '1')
|
||||||
initiator.setAttribute('TargetPortGroup', 'portgroup-test')
|
initiator.setAttribute('TargetPortGroup', 'portgroup-test')
|
||||||
@ -1534,7 +1623,7 @@ class Huawei18000FCDriverTestCase(test.TestCase):
|
|||||||
iscsi.appendChild(defaulttargetip)
|
iscsi.appendChild(defaulttargetip)
|
||||||
initiator = doc.createElement('Initiator')
|
initiator = doc.createElement('Initiator')
|
||||||
initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
|
initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
|
||||||
initiator.setAttribute('TargetIP', '192.168.100.2')
|
initiator.setAttribute('TargetIP', '192.168.1.2')
|
||||||
iscsi.appendChild(initiator)
|
iscsi.appendChild(initiator)
|
||||||
|
|
||||||
prefetch = doc.createElement('Prefetch')
|
prefetch = doc.createElement('Prefetch')
|
||||||
|
@ -15,16 +15,18 @@
|
|||||||
|
|
||||||
STATUS_HEALTH = '1'
|
STATUS_HEALTH = '1'
|
||||||
STATUS_RUNNING = '10'
|
STATUS_RUNNING = '10'
|
||||||
BLOCK_STORAGE_POOL_TYPE = '1'
|
|
||||||
FILE_SYSTEM_POOL_TYPE = '2'
|
|
||||||
STATUS_VOLUME_READY = '27'
|
STATUS_VOLUME_READY = '27'
|
||||||
STATUS_LUNCOPY_READY = '40'
|
STATUS_LUNCOPY_READY = '40'
|
||||||
|
STATUS_QOS_ACTIVE = '2'
|
||||||
|
|
||||||
|
BLOCK_STORAGE_POOL_TYPE = '1'
|
||||||
|
FILE_SYSTEM_POOL_TYPE = '2'
|
||||||
|
|
||||||
HOSTGROUP_PREFIX = 'OpenStack_HostGroup_'
|
HOSTGROUP_PREFIX = 'OpenStack_HostGroup_'
|
||||||
LUNGROUP_PREFIX = 'OpenStack_LunGroup_'
|
LUNGROUP_PREFIX = 'OpenStack_LunGroup_'
|
||||||
MAPPING_VIEW_PREFIX = 'OpenStack_Mapping_View_'
|
MAPPING_VIEW_PREFIX = 'OpenStack_Mapping_View_'
|
||||||
QOS_NAME_PREFIX = 'OpenStack_'
|
QOS_NAME_PREFIX = 'OpenStack_'
|
||||||
|
ARRAY_VERSION = 'V300R003C00'
|
||||||
CAPACITY_UNIT = 1024.0 / 1024.0 / 2
|
CAPACITY_UNIT = 1024.0 / 1024.0 / 2
|
||||||
DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30
|
DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30
|
||||||
DEFAULT_WAIT_INTERVAL = 5
|
DEFAULT_WAIT_INTERVAL = 5
|
||||||
@ -32,7 +34,7 @@ ERROR_CONNECT_TO_SERVER = -403
|
|||||||
ERROR_UNAUTHORIZED_TO_SERVER = -401
|
ERROR_UNAUTHORIZED_TO_SERVER = -401
|
||||||
SOCKET_TIME_OUT = 720
|
SOCKET_TIME_OUT = 720
|
||||||
|
|
||||||
MAX_HOSTNAME_LENTH = 31
|
MAX_HOSTNAME_LENGTH = 31
|
||||||
|
|
||||||
OS_TYPE = {'Linux': '0',
|
OS_TYPE = {'Linux': '0',
|
||||||
'Windows': '1',
|
'Windows': '1',
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import six
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -27,6 +28,7 @@ from cinder.volume import driver
|
|||||||
from cinder.volume.drivers.huawei import constants
|
from cinder.volume.drivers.huawei import constants
|
||||||
from cinder.volume.drivers.huawei import huawei_utils
|
from cinder.volume.drivers.huawei import huawei_utils
|
||||||
from cinder.volume.drivers.huawei import rest_client
|
from cinder.volume.drivers.huawei import rest_client
|
||||||
|
from cinder.volume.drivers.huawei import smartx
|
||||||
from cinder.volume import utils as volume_utils
|
from cinder.volume import utils as volume_utils
|
||||||
from cinder.zonemanager import utils as fczm_utils
|
from cinder.zonemanager import utils as fczm_utils
|
||||||
|
|
||||||
@ -70,6 +72,10 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||||||
@utils.synchronized('huawei', external=True)
|
@utils.synchronized('huawei', external=True)
|
||||||
def create_volume(self, volume):
|
def create_volume(self, volume):
|
||||||
"""Create a volume."""
|
"""Create a volume."""
|
||||||
|
opts = huawei_utils.get_volume_params(volume)
|
||||||
|
smartx_opts = smartx.SmartX().get_smartx_specs_opts(opts)
|
||||||
|
params = huawei_utils.get_lun_params(self.xml_file_path,
|
||||||
|
smartx_opts)
|
||||||
pool_name = volume_utils.extract_host(volume['host'],
|
pool_name = volume_utils.extract_host(volume['host'],
|
||||||
level='pool')
|
level='pool')
|
||||||
pools = self.restclient.find_all_pools()
|
pools = self.restclient.find_all_pools()
|
||||||
@ -88,7 +94,6 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||||||
{'volume': volume_name,
|
{'volume': volume_name,
|
||||||
'size': volume_size})
|
'size': volume_size})
|
||||||
|
|
||||||
params = huawei_utils.get_lun_conf_params(self.xml_file_path)
|
|
||||||
params['pool_id'] = pool_info['ID']
|
params['pool_id'] = pool_info['ID']
|
||||||
params['volume_size'] = volume_size
|
params['volume_size'] = volume_size
|
||||||
params['volume_description'] = volume_description
|
params['volume_description'] = volume_description
|
||||||
@ -99,6 +104,14 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||||||
# Create LUN on the array.
|
# Create LUN on the array.
|
||||||
lun_info = self.restclient.create_volume(lun_param)
|
lun_info = self.restclient.create_volume(lun_param)
|
||||||
lun_id = lun_info['ID']
|
lun_id = lun_info['ID']
|
||||||
|
qos = huawei_utils.get_volume_qos(volume)
|
||||||
|
if qos:
|
||||||
|
smart_qos = smartx.SmartQos(self.restclient)
|
||||||
|
smart_qos.create_qos(qos, lun_id)
|
||||||
|
smartpartition = smartx.SmartPartition(self.restclient)
|
||||||
|
smartpartition.add(opts, lun_id)
|
||||||
|
smartcache = smartx.SmartCache(self.restclient)
|
||||||
|
smartcache.add(opts, lun_id)
|
||||||
|
|
||||||
return {'provider_location': lun_info['ID'],
|
return {'provider_location': lun_info['ID'],
|
||||||
'ID': lun_id,
|
'ID': lun_id,
|
||||||
@ -119,6 +132,10 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||||||
{'name': name, 'lun_id': lun_id},)
|
{'name': name, 'lun_id': lun_id},)
|
||||||
if lun_id:
|
if lun_id:
|
||||||
if self.restclient.check_lun_exist(lun_id):
|
if self.restclient.check_lun_exist(lun_id):
|
||||||
|
qos_id = self.restclient.get_qosid_by_lunid(lun_id)
|
||||||
|
if qos_id:
|
||||||
|
self.remove_qos_lun(lun_id, qos_id)
|
||||||
|
|
||||||
self.restclient.delete_lun(lun_id)
|
self.restclient.delete_lun(lun_id)
|
||||||
else:
|
else:
|
||||||
LOG.warning(_LW("Can't find lun %s on the array."), lun_id)
|
LOG.warning(_LW("Can't find lun %s on the array."), lun_id)
|
||||||
@ -126,6 +143,17 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def remove_qos_lun(self, lun_id, qos_id):
|
||||||
|
lun_list = self.restclient.get_lun_list_in_qos(qos_id)
|
||||||
|
lun_count = len(lun_list)
|
||||||
|
if lun_count <= 1:
|
||||||
|
qos = smartx.SmartQos(self.restclient)
|
||||||
|
qos.delete_qos(qos_id)
|
||||||
|
else:
|
||||||
|
self.restclient.remove_lun_from_qos(lun_id,
|
||||||
|
lun_list,
|
||||||
|
qos_id)
|
||||||
|
|
||||||
def create_volume_from_snapshot(self, volume, snapshot):
|
def create_volume_from_snapshot(self, volume, snapshot):
|
||||||
"""Create a volume from a snapshot.
|
"""Create a volume from a snapshot.
|
||||||
|
|
||||||
@ -238,7 +266,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||||||
def create_snapshot(self, snapshot):
|
def create_snapshot(self, snapshot):
|
||||||
snapshot_info = self.restclient.create_snapshot(snapshot)
|
snapshot_info = self.restclient.create_snapshot(snapshot)
|
||||||
snapshot_id = snapshot_info['ID']
|
snapshot_id = snapshot_info['ID']
|
||||||
self.restclient.active_snapshot(snapshot_id)
|
self.restclient.activate_snapshot(snapshot_id)
|
||||||
|
|
||||||
return {'provider_location': snapshot_info['ID'],
|
return {'provider_location': snapshot_info['ID'],
|
||||||
'lun_info': snapshot_info}
|
'lun_info': snapshot_info}
|
||||||
@ -283,9 +311,9 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||||||
|
|
||||||
host_name_before_hash = None
|
host_name_before_hash = None
|
||||||
host_name = connector['host']
|
host_name = connector['host']
|
||||||
if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENTH):
|
if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
|
||||||
host_name_before_hash = host_name
|
host_name_before_hash = host_name
|
||||||
host_name = str(hash(host_name))
|
host_name = six.text_type(hash(host_name))
|
||||||
|
|
||||||
# Create hostgroup if not exist.
|
# Create hostgroup if not exist.
|
||||||
host_id = self.restclient.add_host_with_check(host_name,
|
host_id = self.restclient.add_host_with_check(host_name,
|
||||||
@ -356,9 +384,9 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||||||
# Create hostgroup if not exist.
|
# Create hostgroup if not exist.
|
||||||
host_name = connector['host']
|
host_name = connector['host']
|
||||||
host_name_before_hash = None
|
host_name_before_hash = None
|
||||||
if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENTH):
|
if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
|
||||||
host_name_before_hash = host_name
|
host_name_before_hash = host_name
|
||||||
host_name = str(hash(host_name))
|
host_name = six.text_type(hash(host_name))
|
||||||
host_id = self.restclient.add_host_with_check(host_name,
|
host_id = self.restclient.add_host_with_check(host_name,
|
||||||
host_name_before_hash)
|
host_name_before_hash)
|
||||||
|
|
||||||
@ -604,6 +632,7 @@ class Huawei18000ISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
|
|||||||
CHAP support
|
CHAP support
|
||||||
Multiple pools support
|
Multiple pools support
|
||||||
ISCSI multipath support
|
ISCSI multipath support
|
||||||
|
SmartX support
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "1.1.1"
|
VERSION = "1.1.1"
|
||||||
@ -640,6 +669,7 @@ class Huawei18000FCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
|||||||
1.1.0 - Provide Huawei OceanStor 18000 storage volume driver
|
1.1.0 - Provide Huawei OceanStor 18000 storage volume driver
|
||||||
1.1.1 - Code refactor
|
1.1.1 - Code refactor
|
||||||
Multiple pools support
|
Multiple pools support
|
||||||
|
SmartX support
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "1.1.1"
|
VERSION = "1.1.1"
|
||||||
|
@ -38,8 +38,8 @@ opts_capability = {
|
|||||||
'smarttier': False,
|
'smarttier': False,
|
||||||
'smartcache': False,
|
'smartcache': False,
|
||||||
'smartpartition': False,
|
'smartpartition': False,
|
||||||
'thin_provisioning': False,
|
'thin_provisioning_support': False,
|
||||||
'thick_provisioning': False,
|
'thick_provisioning_support': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -384,7 +384,7 @@ def wait_for_condition(xml_file_path, func, interval, timeout=None):
|
|||||||
try:
|
try:
|
||||||
res = func()
|
res = func()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
raise exception.VolumeBackendAPIException(ex)
|
raise exception.VolumeBackendAPIException(data=ex)
|
||||||
if res:
|
if res:
|
||||||
raise loopingcall.LoopingCallDone()
|
raise loopingcall.LoopingCallDone()
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ class RestClient(object):
|
|||||||
self._assert_data_in_result(result, msg)
|
self._assert_data_in_result(result, msg)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def find_pool_info(self, pool_name, result):
|
def find_pool_info(self, pool_name=None, result=None):
|
||||||
pool_info = {}
|
pool_info = {}
|
||||||
if not pool_name:
|
if not pool_name:
|
||||||
return pool_info
|
return pool_info
|
||||||
@ -201,11 +201,11 @@ class RestClient(object):
|
|||||||
|
|
||||||
return self._get_id_from_result(result, name, 'NAME')
|
return self._get_id_from_result(result, name, 'NAME')
|
||||||
|
|
||||||
def active_snapshot(self, snapshot_id):
|
def activate_snapshot(self, snapshot_id):
|
||||||
activeurl = self.url + "/snapshot/activate"
|
activate_url = self.url + "/snapshot/activate"
|
||||||
data = json.dumps({"SNAPSHOTLIST": [snapshot_id]})
|
data = json.dumps({"SNAPSHOTLIST": [snapshot_id]})
|
||||||
result = self.call(activeurl, data)
|
result = self.call(activate_url, data)
|
||||||
self._assert_rest_result(result, _('Active snapshot error.'))
|
self._assert_rest_result(result, _('Activate snapshot error.'))
|
||||||
|
|
||||||
def create_snapshot(self, snapshot):
|
def create_snapshot(self, snapshot):
|
||||||
snapshot_name = huawei_utils.encode_name(snapshot['id'])
|
snapshot_name = huawei_utils.encode_name(snapshot['id'])
|
||||||
@ -660,7 +660,7 @@ class RestClient(object):
|
|||||||
|
|
||||||
if 'data' in result:
|
if 'data' in result:
|
||||||
for item in result['data']:
|
for item in result['data']:
|
||||||
if item["ID"] == ininame:
|
if item['ID'] == ininame:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -988,9 +988,9 @@ class RestClient(object):
|
|||||||
|
|
||||||
return iscsi_port_info
|
return iscsi_port_info
|
||||||
|
|
||||||
def _get_tgt_iqn(self, iscsiip):
|
def _get_tgt_iqn(self, iscsi_ip):
|
||||||
"""Get target iSCSI iqn."""
|
"""Get target iSCSI iqn."""
|
||||||
ip_info = self._get_iscsi_port_info(iscsiip)
|
ip_info = self._get_iscsi_port_info(iscsi_ip)
|
||||||
iqn_prefix = self._get_iscsi_tgt_port()
|
iqn_prefix = self._get_iscsi_tgt_port()
|
||||||
if not ip_info:
|
if not ip_info:
|
||||||
err_msg = (_(
|
err_msg = (_(
|
||||||
@ -1016,7 +1016,7 @@ class RestClient(object):
|
|||||||
if iqn_suffix[i] != '0':
|
if iqn_suffix[i] != '0':
|
||||||
iqn_suffix = iqn_suffix[i:]
|
iqn_suffix = iqn_suffix[i:]
|
||||||
break
|
break
|
||||||
iqn = iqn_prefix + ':' + iqn_suffix + ':' + iscsiip
|
iqn = iqn_prefix + ':' + iqn_suffix + ':' + iscsi_ip
|
||||||
LOG.info(_LI('_get_tgt_iqn: iSCSI target iqn is: %s.'), iqn)
|
LOG.info(_LI('_get_tgt_iqn: iSCSI target iqn is: %s.'), iqn)
|
||||||
return iqn
|
return iqn
|
||||||
else:
|
else:
|
||||||
@ -1043,9 +1043,9 @@ class RestClient(object):
|
|||||||
root = huawei_utils.parse_xml_file(self.xml_file_path)
|
root = huawei_utils.parse_xml_file(self.xml_file_path)
|
||||||
pool_names = root.findtext('Storage/StoragePool')
|
pool_names = root.findtext('Storage/StoragePool')
|
||||||
if not pool_names:
|
if not pool_names:
|
||||||
msg = (_(
|
msg = _(
|
||||||
'Invalid resource pool name. '
|
'Invalid resource pool name. '
|
||||||
'Please check the config file.'))
|
'Please check the config file.')
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.InvalidInput(msg)
|
raise exception.InvalidInput(msg)
|
||||||
data = {}
|
data = {}
|
||||||
@ -1054,13 +1054,22 @@ class RestClient(object):
|
|||||||
for pool_name in pool_names.split(";"):
|
for pool_name in pool_names.split(";"):
|
||||||
pool_name = pool_name.strip(' \t\n\r')
|
pool_name = pool_name.strip(' \t\n\r')
|
||||||
capacity = self._get_capacity(pool_name, result)
|
capacity = self._get_capacity(pool_name, result)
|
||||||
pool = {'pool_name': pool_name,
|
pool = {}
|
||||||
'total_capacity_gb': capacity['total_capacity'],
|
pool.update(dict(
|
||||||
'free_capacity_gb': capacity['free_capacity'],
|
pool_name=pool_name,
|
||||||
'reserved_percentage': 0,
|
total_capacity_gb=capacity['total_capacity'],
|
||||||
'QoS_support': True,
|
free_capacity_gb=capacity['free_capacity'],
|
||||||
}
|
reserved_percentage=self.configuration.safe_get(
|
||||||
|
'reserved_percentage'),
|
||||||
|
QoS_support=True,
|
||||||
|
max_over_subscription_ratio=self.configuration.safe_get(
|
||||||
|
'max_over_subscription_ratio'),
|
||||||
|
thin_provisioning_support=True,
|
||||||
|
thick_provisioning_support=True,
|
||||||
|
smarttier=True,
|
||||||
|
smartcache=True,
|
||||||
|
smartpartition=True,
|
||||||
|
))
|
||||||
data['pools'].append(pool)
|
data['pools'].append(pool)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -1083,11 +1092,11 @@ class RestClient(object):
|
|||||||
|
|
||||||
return qos_info
|
return qos_info
|
||||||
|
|
||||||
def _update_qos_policy_lunlist(self, lunlist, policy_id):
|
def _update_qos_policy_lunlist(self, lun_list, policy_id):
|
||||||
url = self.url + "/ioclass/" + policy_id
|
url = self.url + "/ioclass/" + policy_id
|
||||||
data = json.dumps({"TYPE": "230",
|
data = json.dumps({"TYPE": "230",
|
||||||
"ID": policy_id,
|
"ID": policy_id,
|
||||||
"LUNLIST": lunlist})
|
"LUNLIST": lun_list})
|
||||||
result = self.call(url, data, "PUT")
|
result = self.call(url, data, "PUT")
|
||||||
self._assert_rest_result(result, _('Update QoS policy error.'))
|
self._assert_rest_result(result, _('Update QoS policy error.'))
|
||||||
|
|
||||||
@ -1212,18 +1221,19 @@ class RestClient(object):
|
|||||||
result = self.call(url, data, 'DELETE')
|
result = self.call(url, data, 'DELETE')
|
||||||
self._assert_rest_result(result, _('Delete QoS policy error.'))
|
self._assert_rest_result(result, _('Delete QoS policy error.'))
|
||||||
|
|
||||||
def active_deactive_qos(self, qos_id, enablestatus):
|
def activate_deactivate_qos(self, qos_id, enablestatus):
|
||||||
"""Active or deactive QoS.
|
"""Activate or deactivate QoS.
|
||||||
|
|
||||||
enablestatus: true (active)
|
enablestatus: true (activate)
|
||||||
enbalestatus: false (deactive)
|
enbalestatus: false (deactivate)
|
||||||
"""
|
"""
|
||||||
url = self.url + "/ioclass/active/" + qos_id
|
url = self.url + "/ioclass/active/" + qos_id
|
||||||
data = json.dumps({"TYPE": 230,
|
data = json.dumps({"TYPE": 230,
|
||||||
"ID": qos_id,
|
"ID": qos_id,
|
||||||
"ENABLESTATUS": enablestatus})
|
"ENABLESTATUS": enablestatus})
|
||||||
result = self.call(url, data, "PUT")
|
result = self.call(url, data, "PUT")
|
||||||
self._assert_rest_result(result, _('Active or deactive QoS error.'))
|
self._assert_rest_result(
|
||||||
|
result, _('Activate or deactivate QoS error.'))
|
||||||
|
|
||||||
def get_qos_info(self, qos_id):
|
def get_qos_info(self, qos_id):
|
||||||
"""Get QoS information."""
|
"""Get QoS information."""
|
||||||
@ -1235,6 +1245,31 @@ class RestClient(object):
|
|||||||
|
|
||||||
return result['data']
|
return result['data']
|
||||||
|
|
||||||
|
def get_lun_list_in_qos(self, qos_id):
|
||||||
|
"""Get the lun list in QoS."""
|
||||||
|
qos_info = self.get_qos_info(qos_id)
|
||||||
|
lun_list = []
|
||||||
|
lun_string = qos_info['LUNLIST'][1:-1]
|
||||||
|
|
||||||
|
for lun in lun_string.split(","):
|
||||||
|
str = lun[1:-1]
|
||||||
|
lun_list.append(str)
|
||||||
|
|
||||||
|
return lun_list
|
||||||
|
|
||||||
|
def remove_lun_from_qos(self, lun_id, lun_list, qos_id):
|
||||||
|
"""Remove lun from QoS."""
|
||||||
|
lun_list = [i for i in lun_list if i != lun_id]
|
||||||
|
url = self.url + "/ioclass/" + qos_id
|
||||||
|
data = json.dumps({"LUNLIST": lun_list,
|
||||||
|
"TYPE": 230,
|
||||||
|
"ID": qos_id})
|
||||||
|
result = self.call(url, data, "PUT")
|
||||||
|
|
||||||
|
msg = _('Remove lun from Qos error.')
|
||||||
|
self._assert_rest_result(result, msg)
|
||||||
|
self._assert_data_in_result(result, msg)
|
||||||
|
|
||||||
def change_lun_priority(self, lun_id):
|
def change_lun_priority(self, lun_id):
|
||||||
"""Change lun priority to high."""
|
"""Change lun priority to high."""
|
||||||
url = self.url + "/lun/" + lun_id
|
url = self.url + "/lun/" + lun_id
|
||||||
@ -1290,6 +1325,107 @@ class RestClient(object):
|
|||||||
|
|
||||||
return result['data']
|
return result['data']
|
||||||
|
|
||||||
|
def get_partition_id_by_name(self, name):
|
||||||
|
url = self.url + "/cachepartition"
|
||||||
|
result = self.call(url, None, "GET")
|
||||||
|
self._assert_rest_result(result, _('Get partition by name error.'))
|
||||||
|
|
||||||
|
if 'data' in result:
|
||||||
|
for item in result['data']:
|
||||||
|
LOG.debug('get_partition_id_by_name item %(item)s.',
|
||||||
|
{'item': item})
|
||||||
|
if name == item['NAME']:
|
||||||
|
return item['ID']
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_partition_info_by_id(self, partition_id):
|
||||||
|
|
||||||
|
url = self.url + '/cachepartition/' + partition_id
|
||||||
|
data = json.dumps({"TYPE": '268',
|
||||||
|
"ID": partition_id})
|
||||||
|
|
||||||
|
result = self.call(url, data, "GET")
|
||||||
|
self._assert_rest_result(result,
|
||||||
|
_('Get partition by partition id error.'))
|
||||||
|
|
||||||
|
return result['data']
|
||||||
|
|
||||||
|
def add_lun_to_partition(self, lun_id, partition_id):
|
||||||
|
url = self.url + "/lun/associate/cachepartition"
|
||||||
|
data = json.dumps({"ID": partition_id,
|
||||||
|
"ASSOCIATEOBJTYPE": 11,
|
||||||
|
"ASSOCIATEOBJID": lun_id, })
|
||||||
|
result = self.call(url, data, "POST")
|
||||||
|
self._assert_rest_result(result, _('Add lun to partition error.'))
|
||||||
|
|
||||||
|
def get_cache_id_by_name(self, name):
|
||||||
|
url = self.url + "/SMARTCACHEPARTITION"
|
||||||
|
result = self.call(url, None, "GET")
|
||||||
|
self._assert_rest_result(result, _('Get cache by name error.'))
|
||||||
|
|
||||||
|
if 'data' in result:
|
||||||
|
for item in result['data']:
|
||||||
|
if name == item['NAME']:
|
||||||
|
return item['ID']
|
||||||
|
return
|
||||||
|
|
||||||
|
def find_available_qos(self, qos):
|
||||||
|
""""Find available QoS on the array."""
|
||||||
|
qos_id = None
|
||||||
|
lun_list = []
|
||||||
|
url = self.url + "/ioclass?range=[0-100]"
|
||||||
|
result = self.call(url, None, "GET")
|
||||||
|
self._assert_rest_result(result, _('Get QoS information error.'))
|
||||||
|
|
||||||
|
if 'data' in result:
|
||||||
|
for item in result['data']:
|
||||||
|
qos_flag = 0
|
||||||
|
for key in qos:
|
||||||
|
if key not in item:
|
||||||
|
break
|
||||||
|
elif qos[key] != item[key]:
|
||||||
|
break
|
||||||
|
qos_flag = qos_flag + 1
|
||||||
|
if qos_flag == len(qos):
|
||||||
|
qos_id = item['ID']
|
||||||
|
lun_list = item['LUNLIST']
|
||||||
|
break
|
||||||
|
|
||||||
|
return (qos_id, lun_list)
|
||||||
|
|
||||||
|
def add_lun_to_qos(self, qos_id, lun_id, lun_list):
|
||||||
|
url = self.url + "/ioclass/" + qos_id
|
||||||
|
lun_list = []
|
||||||
|
lun_string = lun_list[1:-1]
|
||||||
|
for lun in lun_string.split(","):
|
||||||
|
str = lun[1:-1]
|
||||||
|
lun_list.append(str)
|
||||||
|
lun_list.append(lun_id)
|
||||||
|
data = json.dumps({"LUNLIST": lun_list,
|
||||||
|
"TYPE": 230,
|
||||||
|
"ID": qos_id})
|
||||||
|
result = self.call(url, data, "PUT")
|
||||||
|
msg = _('Associate lun to Qos error.')
|
||||||
|
self._assert_rest_result(result, msg)
|
||||||
|
self._assert_data_in_result(result, msg)
|
||||||
|
|
||||||
|
def add_lun_to_cache(self, lun_id, cache_id):
|
||||||
|
url = self.url + "/SMARTCACHEPARTITION/CREATE_ASSOCIATE"
|
||||||
|
data = json.dumps({"ID": cache_id,
|
||||||
|
"ASSOCIATEOBJTYPE": 11,
|
||||||
|
"ASSOCIATEOBJID": lun_id,
|
||||||
|
"TYPE": 273})
|
||||||
|
result = self.call(url, data, "PUT")
|
||||||
|
|
||||||
|
self._assert_rest_result(result, _('Add lun to cache error.'))
|
||||||
|
|
||||||
|
def find_array_version(self):
|
||||||
|
url = self.url + "/system/"
|
||||||
|
result = self.call(url, None)
|
||||||
|
self._assert_rest_result(result, _('Find array version error.'))
|
||||||
|
return result['data']['PRODUCTVERSION']
|
||||||
|
|
||||||
def remove_host(self, host_id):
|
def remove_host(self, host_id):
|
||||||
url = self.url + "/host/%s" % host_id
|
url = self.url + "/host/%s" % host_id
|
||||||
result = self.call(url, None, "DELETE")
|
result = self.call(url, None, "DELETE")
|
||||||
|
161
cinder/volume/drivers/huawei/smartx.py
Normal file
161
cinder/volume/drivers/huawei/smartx.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import excutils
|
||||||
|
|
||||||
|
from cinder import exception
|
||||||
|
from cinder.i18n import _
|
||||||
|
from cinder.volume.drivers.huawei import constants
|
||||||
|
from cinder.volume.drivers.huawei import huawei_utils
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SmartQos(object):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
def create_qos(self, qos, lun_id):
|
||||||
|
policy_id = None
|
||||||
|
try:
|
||||||
|
# Check QoS priority.
|
||||||
|
if huawei_utils.check_qos_high_priority(qos):
|
||||||
|
self.client.change_lun_priority(lun_id)
|
||||||
|
# Create QoS policy and activate it.
|
||||||
|
version = self.client.find_array_version()
|
||||||
|
if version >= constants.ARRAY_VERSION:
|
||||||
|
(qos_id, lun_list) = self.client.find_available_qos(qos)
|
||||||
|
if qos_id:
|
||||||
|
self.client.add_lun_to_qos(qos_id, lun_id, lun_list)
|
||||||
|
else:
|
||||||
|
policy_id = self.client.create_qos_policy(qos, lun_id)
|
||||||
|
self.client.activate_deactivate_qos(policy_id, True)
|
||||||
|
else:
|
||||||
|
policy_id = self.client.create_qos_policy(qos, lun_id)
|
||||||
|
self.client.activate_deactivate_qos(policy_id, True)
|
||||||
|
except exception.VolumeBackendAPIException:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
if policy_id is not None:
|
||||||
|
self.client.delete_qos_policy(policy_id)
|
||||||
|
|
||||||
|
def delete_qos(self, qos_id):
|
||||||
|
qos_info = self.client.get_qos_info(qos_id)
|
||||||
|
qos_status = qos_info['RUNNINGSTATUS']
|
||||||
|
# 2: Active status.
|
||||||
|
if qos_status == constants.STATUS_QOS_ACTIVE:
|
||||||
|
self.client.activate_deactivate_qos(qos_id, False)
|
||||||
|
self.client.delete_qos_policy(qos_id)
|
||||||
|
|
||||||
|
|
||||||
|
class SmartPartition(object):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
def add(self, opts, lun_id):
|
||||||
|
if opts['smartpartition'] != 'true':
|
||||||
|
return
|
||||||
|
if not opts['partitionname']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('Partition name is None, please set '
|
||||||
|
'smartpartition:partitionname in key.'))
|
||||||
|
|
||||||
|
partition_id = self.client.get_partition_id_by_name(
|
||||||
|
opts['partitionname'])
|
||||||
|
if not partition_id:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=(_('Can not find partition id by name %(name)s.')
|
||||||
|
% {'name': opts['partitionname']}))
|
||||||
|
|
||||||
|
self.client.add_lun_to_partition(lun_id, partition_id)
|
||||||
|
|
||||||
|
|
||||||
|
class SmartCache(object):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
def add(self, opts, lun_id):
|
||||||
|
if opts['smartcache'] != 'true':
|
||||||
|
return
|
||||||
|
if not opts['cachename']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('Cache name is None, please set '
|
||||||
|
'smartcache:cachename in key.'))
|
||||||
|
|
||||||
|
cache_id = self.client.get_cache_id_by_name(opts['cachename'])
|
||||||
|
if not cache_id:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=(_('Can not find cache id by cache name %(name)s.')
|
||||||
|
% {'name': opts['cachename']}))
|
||||||
|
|
||||||
|
self.client.add_lun_to_cache(lun_id, cache_id)
|
||||||
|
|
||||||
|
|
||||||
|
class SmartX(object):
|
||||||
|
def get_smartx_specs_opts(self, opts):
|
||||||
|
# Check that smarttier is 0/1/2/3
|
||||||
|
opts = self.get_smarttier_opts(opts)
|
||||||
|
opts = self.get_smartthin_opts(opts)
|
||||||
|
opts = self.get_smartcache_opts(opts)
|
||||||
|
opts = self.get_smartpartition_opts(opts)
|
||||||
|
return opts
|
||||||
|
|
||||||
|
def get_smarttier_opts(self, opts):
|
||||||
|
if opts['smarttier'] == 'true':
|
||||||
|
if not opts['policy']:
|
||||||
|
opts['policy'] = '1'
|
||||||
|
elif opts['policy'] not in ['0', '1', '2', '3']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=(_('Illegal value specified for smarttier: '
|
||||||
|
'set to either 0, 1, 2, or 3.')))
|
||||||
|
else:
|
||||||
|
opts['policy'] = '0'
|
||||||
|
|
||||||
|
return opts
|
||||||
|
|
||||||
|
def get_smartthin_opts(self, opts):
|
||||||
|
if opts['thin_provisioning_support'] == 'true':
|
||||||
|
if opts['thick_provisioning_support'] == 'true':
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=(_('Illegal value specified for thin: '
|
||||||
|
'Can not set thin and thick at the same time.')))
|
||||||
|
else:
|
||||||
|
opts['LUNType'] = 1
|
||||||
|
if opts['thick_provisioning_support'] == 'true':
|
||||||
|
opts['LUNType'] = 0
|
||||||
|
|
||||||
|
return opts
|
||||||
|
|
||||||
|
def get_smartcache_opts(self, opts):
|
||||||
|
if opts['smartcache'] == 'true':
|
||||||
|
if not opts['cachename']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('Cache name is None, please set '
|
||||||
|
'smartcache:cachename in key.'))
|
||||||
|
else:
|
||||||
|
opts['cachename'] = None
|
||||||
|
|
||||||
|
return opts
|
||||||
|
|
||||||
|
def get_smartpartition_opts(self, opts):
|
||||||
|
if opts['smartpartition'] == 'true':
|
||||||
|
if not opts['partitionname']:
|
||||||
|
raise exception.InvalidInput(
|
||||||
|
reason=_('Partition name is None, please set '
|
||||||
|
'smartpartition:partitionname in key.'))
|
||||||
|
else:
|
||||||
|
opts['partitionname'] = None
|
||||||
|
|
||||||
|
return opts
|
Loading…
x
Reference in New Issue
Block a user