Huawei driver refactor(1/10)

Huawei driver code is terribly decayed and hard to maintain.
From this patch on, we'll try to progressively refactor total
Huawei driver, make the code more clear, reliable and maintainable.

This patch mainly optimized the huawei_conf.py file and the logic
that according to it.

Change-Id: I5da3e2a8b1bae4502ae4025a931e9c6899c81e56
This commit is contained in:
zengyingzhe 2019-01-21 17:22:08 +08:00
parent 1c0224d901
commit 45caa24e1b
7 changed files with 453 additions and 352 deletions

View File

@ -2110,28 +2110,33 @@ class FakeHuaweiConf(huawei_conf.HuaweiConf):
setattr(self.conf, 'metro_san_password', 'Admin@storage1')
setattr(self.conf, 'metro_domain_name', 'hypermetro_test')
iscsi_info = {'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'TargetIP': '192.0.2.2',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
'TargetPortGroup': 'portgroup-test', }
setattr(self.conf, 'iscsi_info', [iscsi_info])
iscsi_info = {
'default_target_ips': '192.0.2.2',
'initiators': {
'iqn.1993-08.debian:01:ec2bff7ac3a3': {
'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
'TargetPortGroup': 'portgroup-test',
}
}
}
setattr(self.conf, 'iscsi_info', iscsi_info)
rmt_iscsi_info = ('{ Name: iqn.1993-08.debian:01:ec2bff7acxxx;\n'
'TargetIP:1.1.1.1;CHAPinfo:mm-user#mm-user@storage;'
'ALUA:1; TargetPortGroup:portgroup-test};\t\n '
'{ Name: iqn.1993-08.debian:01:ec2bff7acyyy;\n'
'TargetIP:2.2.2.2;CHAPinfo:nn-user#nn-user@storage;'
'ALUA:0; TargetPortGroup:portgroup-test1}\t\n')
rmt_iscsi_info = {'Name': 'iqn.1993-08.debian:01:ec2bff7acxxx',
'TargetIP': '1.1.1.1',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
'TargetPortGroup': 'portgroup-test'}
targets = [{'backend_id': REPLICA_BACKEND_ID,
'storage_pool': 'OpenStack_Pool',
'san_address':
'https://192.0.2.69:8088/deviceManager/rest/',
'san_user': 'admin',
'san_password': 'Admin@storage1',
'iscsi_info': rmt_iscsi_info}]
setattr(self.conf, 'replication_device', targets)
target = {'backend_id': REPLICA_BACKEND_ID,
'storage_pool': 'OpenStack_Pool',
'san_address':
'https://192.0.2.69:8088/deviceManager/rest/',
'san_user': 'admin',
'san_password': 'Admin@storage1',
'iscsi_info': rmt_iscsi_info}
setattr(self.conf, 'replication', target)
setattr(self.conf, 'safe_get', self.safe_get)
@ -2545,25 +2550,13 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
self.driver.client.login()
def test_parse_rmt_iscsi_info(self):
rmt_devs = self.driver.huawei_conf.get_replication_devices()
iscsi_info = rmt_devs[0]['iscsi_info']
expected_iscsi_info = [{'Name': 'iqn.1993-08.debian:01:ec2bff7acxxx',
'TargetIP': '1.1.1.1',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
'TargetPortGroup': 'portgroup-test'},
{'Name': 'iqn.1993-08.debian:01:ec2bff7acyyy',
'TargetIP': '2.2.2.2',
'CHAPinfo': 'nn-user;nn-user@storage',
'ALUA': '0',
'TargetPortGroup': 'portgroup-test1'}]
self.assertEqual(expected_iscsi_info, iscsi_info)
def test_parse_rmt_iscsi_info_without_iscsi_configuration(self):
self.configuration.replication_device[0]['iscsi_info'] = ''
rmt_devs = self.driver.huawei_conf.get_replication_devices()
iscsi_info = rmt_devs[0]['iscsi_info']
self.assertEqual([], iscsi_info)
rmt_dev = self.driver.configuration.replication
expected_iscsi_info = {'Name': 'iqn.1993-08.debian:01:ec2bff7acxxx',
'TargetIP': '1.1.1.1',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
'TargetPortGroup': 'portgroup-test'}
self.assertDictEqual(expected_iscsi_info, rmt_dev['iscsi_info'])
def test_login_success(self):
device_id = self.driver.client.login()
@ -2849,7 +2842,7 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
temp_connector = copy.deepcopy(FakeConnector)
temp_connector['multipath'] = True
self.mock_object(rest_client.RestClient, 'get_tgt_port_group',
return_value = '11')
return_value='11')
iscsi_properties = self.driver.initialize_connection(self.volume,
temp_connector)
self.assertEqual([1, 1], iscsi_properties['data']['target_luns'])
@ -2858,22 +2851,28 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
temp_connector = copy.deepcopy(FakeConnector)
temp_connector['multipath'] = True
self.mock_object(rest_client.RestClient, 'get_tgt_port_group',
return_value = '12')
return_value='12')
self.mock_object(rest_client.RestClient, '_get_tgt_ip_from_portgroup',
return_value = [])
return_value=[])
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
self.volume, temp_connector)
def test_initialize_connection_success_multipath_targetip(self):
iscsi_info = [{'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'TargetIP': '192.0.2.2',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1'}]
iscsi_info = {
'initiators': {
'iqn.1993-08.debian:01:ec2bff7ac3a3': {
'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'TargetIP': '192.0.2.2',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
}
}
}
configuration = mock.Mock(spec = conf.Configuration)
configuration = mock.Mock(spec=conf.Configuration)
configuration.hypermetro_devices = hypermetro_devices
driver = FakeISCSIStorage(configuration = self.configuration)
driver = FakeISCSIStorage(configuration=self.configuration)
driver.do_setup()
driver.configuration.iscsi_info = iscsi_info
driver.client.iscsi_info = iscsi_info
@ -2884,14 +2883,20 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
self.assertEqual([1], iscsi_properties['data']['target_luns'])
def test_initialize_connection_fail_multipath_targetip(self):
iscsi_info = [{'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'TargetIP': '192.0.2.6',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1'}]
iscsi_info = {
'initiators': {
'iqn.1993-08.debian:01:ec2bff7ac3a3': {
'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'TargetIP': '192.0.2.6',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
}
}
}
configuration = mock.Mock(spec = conf.Configuration)
configuration = mock.Mock(spec=conf.Configuration)
configuration.hypermetro_devices = hypermetro_devices
driver = FakeISCSIStorage(configuration = self.configuration)
driver = FakeISCSIStorage(configuration=self.configuration)
driver.do_setup()
driver.configuration.iscsi_info = iscsi_info
driver.client.iscsi_info = iscsi_info
@ -2902,18 +2907,23 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
self.volume, temp_connector)
def test_initialize_connection_success_multipath_defaultip(self):
iscsi_info = [{'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1'}]
default_target_ip = ['192.0.2.2']
configuration = mock.Mock(spec = conf.Configuration)
iscsi_info = {
'default_target_ips': ['192.0.2.2'],
'initiators': {
'iqn.1993-08.debian:01:ec2bff7ac3a3': {
'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
}
}
}
configuration = mock.Mock(spec=conf.Configuration)
configuration.hypermetro_devices = hypermetro_devices
driver = FakeISCSIStorage(configuration = self.configuration)
driver = FakeISCSIStorage(configuration=self.configuration)
driver.do_setup()
driver.configuration.iscsi_info = iscsi_info
driver.client.iscsi_info = iscsi_info
driver.configuration.iscsi_default_target_ip = default_target_ip
driver.client.iscsi_default_target_ip = default_target_ip
temp_connector = copy.deepcopy(FakeConnector)
temp_connector['multipath'] = True
iscsi_properties = driver.initialize_connection(self.volume,
@ -2921,19 +2931,23 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
self.assertEqual([1], iscsi_properties['data']['target_luns'])
def test_initialize_connection_fail_multipath_defaultip(self):
iscsi_info = [{'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1'}]
iscsi_info = {
'default_target_ips': ['192.0.2.6'],
'initiators': {
'iqn.1993-08.debian:01:ec2bff7ac3a3': {
'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
}
},
}
default_target_ip = ['192.0.2.6']
configuration = mock.Mock(spec = conf.Configuration)
configuration = mock.Mock(spec=conf.Configuration)
configuration.hypermetro_devices = hypermetro_devices
driver = FakeISCSIStorage(configuration = self.configuration)
driver = FakeISCSIStorage(configuration=self.configuration)
driver.do_setup()
driver.configuration.iscsi_info = iscsi_info
driver.client.iscsi_info = iscsi_info
driver.configuration.iscsi_default_target_ip = default_target_ip
driver.client.iscsi_default_target_ip = default_target_ip
temp_connector = copy.deepcopy(FakeConnector)
temp_connector['multipath'] = True
self.assertRaises(exception.VolumeBackendAPIException,
@ -2952,17 +2966,23 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
self.volume, temp_connector)
def test_initialize_connection_fail_multipath_no_ip(self):
iscsi_info = [{'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1'}]
configuration = mock.Mock(spec = conf.Configuration)
iscsi_info = {
'default_target_ips': [],
'initiators': {
'iqn.1993-08.debian:01:ec2bff7ac3a3': {
'Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
'CHAPinfo': 'mm-user;mm-user@storage',
'ALUA': '1',
}
}
}
configuration = mock.Mock(spec=conf.Configuration)
configuration.hypermetro_devices = hypermetro_devices
driver = FakeISCSIStorage(configuration = self.configuration)
driver = FakeISCSIStorage(configuration=self.configuration)
driver.do_setup()
driver.configuration.iscsi_info = iscsi_info
driver.client.iscsi_info = iscsi_info
driver.configuration.iscsi_default_target_ip = None
driver.client.iscsi_default_target_ip = None
temp_connector = copy.deepcopy(FakeConnector)
temp_connector['multipath'] = True
self.assertRaises(exception.VolumeBackendAPIException,
@ -3082,25 +3102,31 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
self.assertEqual(self.target_ips, target_ip)
def test_find_chap_info(self):
tmp_dict = {}
tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
tmp_dict['CHAPinfo'] = 'mm-user;mm-user@storage'
iscsi_info = [tmp_dict]
initiator_name = FakeConnector['initiator']
chapinfo = self.driver.client.find_chap_info(iscsi_info,
initiator_name)
iscsi_info = {
'initiators': {
'fake.iqn': {
'Name': 'fake.iqn',
'CHAPinfo': 'mm-user;mm-user@storage',
}
}
}
chapinfo = self.driver.client.find_chap_info(iscsi_info, 'fake.iqn')
chap_username, chap_password = chapinfo.split(';')
self.assertEqual('mm-user', chap_username)
self.assertEqual('mm-user@storage', chap_password)
def test_find_alua_info(self):
tmp_dict = {}
tmp_dict['Name'] = 'iqn.1993-08.debian:01:ec2bff7ac3a3'
tmp_dict['ALUA'] = '1'
iscsi_info = [tmp_dict]
initiator_name = FakeConnector['initiator']
type = self.driver.client._find_alua_info(iscsi_info,
initiator_name)
iscsi_info = {
'initiators': {
'fake.iqn': {
'Name': 'fake.iqn',
'ALUA': '1',
}
}
}
type = self.driver.client._find_alua_info(iscsi_info, 'fake.iqn')
self.assertEqual('1', type)
def test_get_pool_info(self):
@ -5630,11 +5656,7 @@ class HuaweiConfTestCase(test.TestCase):
'san_user': 'admin',
'san_password': '123456',
'storage_pool': 'OpenStack_Pool',
'iscsi_info': """{Name:iqn.1993-08.debian:01:ec2bff7acxxx;
TargetIP:1.1.1.1;
CHAPinfo:mm-user@storage;
ALUA:1;
TargetPortGroup:portgroup-test}"""
'iscsi_info': '{Name:iqn;CHAPinfo:user#pwd;ALUA:1}'
}]
)
def test_get_replication_devices(self, config):
@ -5643,21 +5665,29 @@ class HuaweiConfTestCase(test.TestCase):
mock.Mock(return_value=config)
)
replication_devices = self.huawei_conf.get_replication_devices()
expected = [
{'backend_id': 'default',
'iscsi_default_target_ip': [],
'iscsi_info': [{'ALUA': '1',
'CHAPinfo': 'mm-user@storage',
'Name': 'iqn.1993-08.debian:01:ec2bff7acxxx',
'TargetIP': '1.1.1.1',
'TargetPortGroup': 'portgroup-test'}],
'san_address': ['https://192.0.2.69:8088/deviceManager/rest/'],
'san_password': '123456',
'san_user': 'admin',
'storage_pools': ['OpenStack_Pool']}]
self.huawei_conf._replication_devices(None)
expected = {
'backend_id': 'default',
'san_address': ['https://192.0.2.69:8088/deviceManager/rest/'],
'san_password': '123456',
'san_user': 'admin',
'storage_pools': ['OpenStack_Pool'],
'vstore_name': None,
'iscsi_info': {
'initiators': {
'iqn': {'ALUA': '1',
'CHAPinfo': 'user;pwd',
'Name': 'iqn'}
},
'default_target_ips': [],
},
'fc_info': {
'initiators': {},
'default_target_ips': [],
},
}
self.assertEqual(expected, replication_devices)
self.assertDictEqual(expected, self.conf.replication)
@ddt.ddt

View File

@ -105,11 +105,16 @@ class HuaweiBaseDriver(driver.VolumeDriver):
return False
def get_local_and_remote_dev_conf(self):
self.loc_dev_conf = self.huawei_conf.get_local_device()
self.loc_dev_conf = {
'san_address': self.configuration.san_address,
'san_user': self.configuration.san_user,
'san_password': self.configuration.san_password,
'storage_pools': self.configuration.storage_pools,
'iscsi_info': self.configuration.iscsi_info,
}
# Now just support one replication device.
replica_devs = self.huawei_conf.get_replication_devices()
self.replica_dev_conf = replica_devs[0] if replica_devs else {}
self.replica_dev_conf = self.configuration.replication
def get_local_and_remote_client_conf(self):
if self.active_backend_id:
@ -310,13 +315,18 @@ class HuaweiBaseDriver(driver.VolumeDriver):
'DESCRIPTION': volume.name,
'ALLOCTYPE': opts.get('LUNType', self.configuration.lun_type),
'CAPACITY': huawei_utils.get_volume_size(volume),
'WRITEPOLICY': self.configuration.lun_write_type,
'PREFETCHPOLICY': self.configuration.lun_prefetch_type,
'PREFETCHVALUE': self.configuration.lun_prefetch_value,
'DATATRANSFERPOLICY':
opts.get('policy', self.configuration.lun_policy),
'READCACHEPOLICY': self.configuration.lun_read_cache_policy,
'WRITECACHEPOLICY': self.configuration.lun_write_cache_policy, }
'WRITECACHEPOLICY': self.configuration.lun_write_cache_policy,
}
if hasattr(self.configuration, 'write_type'):
params['WRITEPOLICY'] = self.configuration.write_type
if hasattr(self.configuration, 'prefetch_type'):
params['PREFETCHPOLICY'] = self.configuration.prefetch_type
if hasattr(self.configuration, 'prefetch_value'):
params['PREFETCHVALUE'] = self.configuration.prefetch_value
if opts.get('policy'):
params['DATATRANSFERPOLICY'] = opts['policy']
LOG.info('volume: %(volume)s, lun params: %(params)s.',
{'volume': volume.id, 'params': params})

View File

@ -116,6 +116,8 @@ REPLICA_DATA_STATUS_INCOMPLETE = '3'
LUN_TYPE_MAP = {'Thick': THICK_LUNTYPE,
'Thin': THIN_LUNTYPE}
VALID_PRODUCT = ('V3', 'V5', '18000', 'Dorado')
PRODUCT_LUN_TYPE = {
'Dorado': 'Thin',
}

View File

@ -22,13 +22,14 @@ and set every property into Configuration object as an attribute.
import base64
from defusedxml import ElementTree as ET
import os
import re
import six
from oslo_log import log as logging
from cinder import exception
from cinder.i18n import _
from cinder import utils
from cinder.volume.drivers.huawei import constants
LOG = logging.getLogger(__name__)
@ -37,55 +38,69 @@ LOG = logging.getLogger(__name__)
class HuaweiConf(object):
def __init__(self, conf):
self.conf = conf
self.last_modify_time = None
def _encode_authentication(self):
need_encode = False
def update_config_value(self):
file_time = os.stat(self.conf.cinder_huawei_conf_file).st_mtime
if self.last_modify_time == file_time:
return
self.last_modify_time = file_time
tree = ET.parse(self.conf.cinder_huawei_conf_file)
xml_root = tree.getroot()
self._encode_authentication(tree, xml_root)
attr_funcs = (
self._san_address,
self._san_user,
self._san_password,
self._san_vstore,
self._san_product,
self._ssl_cert_path,
self._ssl_cert_verify,
self._iscsi_info,
self._fc_info,
self._hypermetro_devices,
self._replication_devices,
self._lun_type,
self._lun_ready_wait_interval,
self._lun_copy_wait_interval,
self._lun_timeout,
self._lun_write_type,
self._lun_prefetch,
self._lun_policy,
self._lun_read_cache_policy,
self._lun_write_cache_policy,
self._storage_pools,
)
for f in attr_funcs:
f(xml_root)
def _encode_authentication(self, tree, xml_root):
name_node = xml_root.find('Storage/UserName')
pwd_node = xml_root.find('Storage/UserPassword')
if (name_node is not None
and not name_node.text.startswith('!$$$')):
name_node.text = '!$$$' + base64.b64encode(name_node.text)
vstore_node = xml_root.find('Storage/vStoreName')
need_encode = False
if name_node is not None and not name_node.text.startswith('!$$$'):
encoded = base64.b64encode(six.b(name_node.text)).decode()
name_node.text = '!$$$' + encoded
need_encode = True
if (pwd_node is not None
and not pwd_node.text.startswith('!$$$')):
pwd_node.text = '!$$$' + base64.b64encode(pwd_node.text)
if pwd_node is not None and not pwd_node.text.startswith('!$$$'):
encoded = base64.b64encode(six.b(pwd_node.text)).decode()
pwd_node.text = '!$$$' + encoded
need_encode = True
if vstore_node is not None and not vstore_node.text.startswith('!$$$'):
encoded = base64.b64encode(six.b(vstore_node.text)).decode()
vstore_node.text = '!$$$' + encoded
need_encode = True
if need_encode:
utils.execute('chmod',
'600',
self.conf.cinder_huawei_conf_file,
run_as_root=True)
tree.write(self.conf.cinder_huawei_conf_file, 'UTF-8')
def update_config_value(self):
self._encode_authentication()
set_attr_funcs = (self._san_address,
self._san_user,
self._san_password,
self._san_product,
self._san_protocol,
self._lun_type,
self._lun_ready_wait_interval,
self._lun_copy_wait_interval,
self._lun_timeout,
self._lun_write_type,
self._lun_prefetch,
self._lun_policy,
self._lun_read_cache_policy,
self._lun_write_cache_policy,
self._storage_pools,
self._iscsi_default_target_ip,
self._iscsi_info,)
tree = ET.parse(self.conf.cinder_huawei_conf_file)
xml_root = tree.getroot()
for f in set_attr_funcs:
f(xml_root)
def _san_address(self, xml_root):
text = xml_root.findtext('Storage/RestURL')
if not text:
@ -93,8 +108,7 @@ class HuaweiConf(object):
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
addrs = text.split(';')
addrs = list(set([x.strip() for x in addrs if x.strip()]))
addrs = list(set([x.strip() for x in text.split(';') if x.strip()]))
setattr(self.conf, 'san_address', addrs)
def _san_user(self, xml_root):
@ -104,7 +118,7 @@ class HuaweiConf(object):
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
user = base64.b64decode(text[4:])
user = base64.b64decode(six.b(text[4:])).decode()
setattr(self.conf, 'san_user', user)
def _san_password(self, xml_root):
@ -114,9 +128,52 @@ class HuaweiConf(object):
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
pwd = base64.b64decode(text[4:])
pwd = base64.b64decode(six.b(text[4:])).decode()
setattr(self.conf, 'san_password', pwd)
def _san_vstore(self, xml_root):
vstore = None
text = xml_root.findtext('Storage/vStoreName')
if text:
vstore = base64.b64decode(six.b(text[4:])).decode()
setattr(self.conf, 'vstore_name', vstore)
def _ssl_cert_path(self, xml_root):
text = xml_root.findtext('Storage/SSLCertPath')
setattr(self.conf, 'ssl_cert_path', text)
def _ssl_cert_verify(self, xml_root):
value = False
text = xml_root.findtext('Storage/SSLCertVerify')
if text:
if text.lower() in ('true', 'false'):
value = text.lower() == 'true'
else:
msg = _("SSLCertVerify configured error.")
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
setattr(self.conf, 'ssl_cert_verify', value)
def _set_extra_constants_by_product(self, product):
extra_constants = {}
if product == 'Dorado':
extra_constants['QOS_SPEC_KEYS'] = (
'maxIOPS', 'maxBandWidth', 'IOType')
extra_constants['QOS_IOTYPES'] = ('2',)
extra_constants['SUPPORT_LUN_TYPES'] = ('Thin',)
extra_constants['DEFAULT_LUN_TYPE'] = 'Thin'
else:
extra_constants['QOS_SPEC_KEYS'] = (
'maxIOPS', 'minIOPS', 'minBandWidth',
'maxBandWidth', 'latency', 'IOType')
extra_constants['QOS_IOTYPES'] = ('0', '1', '2')
extra_constants['SUPPORT_LUN_TYPES'] = ('Thick', 'Thin')
extra_constants['DEFAULT_LUN_TYPE'] = 'Thick'
for k in extra_constants:
setattr(constants, k, extra_constants[k])
def _san_product(self, xml_root):
text = xml_root.findtext('Storage/Product')
if not text:
@ -125,47 +182,36 @@ class HuaweiConf(object):
raise exception.InvalidInput(reason=msg)
product = text.strip()
setattr(self.conf, 'san_product', product)
def _san_protocol(self, xml_root):
text = xml_root.findtext('Storage/Protocol')
if not text:
msg = _("SAN protocol is not configured.")
if product not in constants.VALID_PRODUCT:
msg = _("Invalid SAN product %(text)s, SAN product must be "
"in %(valid)s.") % {'text': product,
'valid': constants.VALID_PRODUCT}
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
protocol = text.strip()
setattr(self.conf, 'san_protocol', protocol)
self._set_extra_constants_by_product(product)
setattr(self.conf, 'san_product', product)
def _lun_type(self, xml_root):
lun_type = constants.PRODUCT_LUN_TYPE.get(self.conf.san_product,
'Thick')
def _verify_conf_lun_type(lun_type):
lun_type = constants.DEFAULT_LUN_TYPE
text = xml_root.findtext('LUN/LUNType')
if text:
lun_type = text.strip()
if lun_type not in constants.LUN_TYPE_MAP:
msg = _("Invalid lun type %s is configured.") % lun_type
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
if self.conf.san_product in constants.PRODUCT_LUN_TYPE:
product_lun_type = constants.PRODUCT_LUN_TYPE[
self.conf.san_product]
if lun_type != product_lun_type:
msg = _("%(array)s array requires %(valid)s lun type, "
"but %(conf)s is specified.") % {
'array': self.conf.san_product,
'valid': product_lun_type,
'conf': lun_type}
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
if lun_type not in constants.SUPPORT_LUN_TYPES:
msg = _("%(array)s array requires %(valid)s lun type, "
"but %(conf)s is specified."
) % {'array': self.conf.san_product,
'valid': constants.SUPPORT_LUN_TYPES,
'conf': lun_type}
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
text = xml_root.findtext('LUN/LUNType')
if text:
lun_type = text.strip()
_verify_conf_lun_type(lun_type)
lun_type = constants.LUN_TYPE_MAP[lun_type]
setattr(self.conf, 'lun_type', lun_type)
setattr(self.conf, 'lun_type', constants.LUN_TYPE_MAP[lun_type])
def _lun_ready_wait_interval(self, xml_root):
text = xml_root.findtext('LUN/LUNReadyWaitInterval')
@ -184,33 +230,19 @@ class HuaweiConf(object):
def _lun_write_type(self, xml_root):
text = xml_root.findtext('LUN/WriteType')
write_type = text.strip() if text else '1'
setattr(self.conf, 'lun_write_type', write_type)
if text and text.strip():
setattr(self.conf, 'write_type', text.strip())
def _lun_prefetch(self, xml_root):
prefetch_type = '3'
prefetch_value = '0'
node = xml_root.find('LUN/Prefetch')
if (node is not None
and node.attrib['Type']
and node.attrib['Value']):
prefetch_type = node.attrib['Type'].strip()
if prefetch_type not in ['0', '1', '2', '3']:
msg = (_(
"Invalid prefetch type '%s' is configured. "
"PrefetchType must be in 0,1,2,3.") % prefetch_type)
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
if node is not None:
if 'Type' in node.attrib:
prefetch_type = node.attrib['Type'].strip()
setattr(self.conf, 'prefetch_type', prefetch_type)
prefetch_value = node.attrib['Value'].strip()
factor = {'1': 2}
factor = int(factor.get(prefetch_type, '1'))
prefetch_value = int(prefetch_value) * factor
prefetch_value = six.text_type(prefetch_value)
setattr(self.conf, 'lun_prefetch_type', prefetch_type)
setattr(self.conf, 'lun_prefetch_value', prefetch_value)
if 'Value' in node.attrib:
prefetch_value = node.attrib['Value'].strip()
setattr(self.conf, 'prefetch_value', prefetch_value)
def _lun_policy(self, xml_root):
setattr(self.conf, 'lun_policy', '0')
@ -222,118 +254,150 @@ class HuaweiConf(object):
setattr(self.conf, 'lun_write_cache_policy', '5')
def _storage_pools(self, xml_root):
nodes = xml_root.findall('LUN/StoragePool')
if not nodes:
text = xml_root.findtext('LUN/StoragePool')
if not text:
msg = _('Storage pool is not configured.')
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
texts = [x.text for x in nodes]
merged_text = ';'.join(texts)
pools = set(x.strip() for x in merged_text.split(';') if x.strip())
pools = set(x.strip() for x in text.split(';') if x.strip())
if not pools:
msg = _('Invalid storage pool is configured.')
msg = _('No valid storage pool configured.')
LOG.error(msg)
raise exception.InvalidInput(msg)
setattr(self.conf, 'storage_pools', list(pools))
def _iscsi_default_target_ip(self, xml_root):
text = xml_root.findtext('iSCSI/DefaultTargetIP')
target_ip = text.split() if text else []
setattr(self.conf, 'iscsi_default_target_ip', target_ip)
def _iscsi_info(self, xml_root):
iscsi_info = {
'default_target_ips': [],
'CHAPinfo': xml_root.findtext('iSCSI/CHAPinfo'),
'ALUA': xml_root.findtext('iSCSI/ALUA'),
'FAILOVERMODE': xml_root.findtext('iSCSI/FAILOVERMODE'),
'SPECIALMODETYPE': xml_root.findtext('iSCSI/SPECIALMODETYPE'),
'PATHTYPE': xml_root.findtext('iSCSI/PATHTYPE'),
}
text = xml_root.findtext('iSCSI/DefaultTargetIP')
if text:
iscsi_info['default_target_ips'] = [
ip.strip() for ip in text.split(';') if ip.strip()]
initiators = {}
nodes = xml_root.findall('iSCSI/Initiator')
if nodes is None:
setattr(self.conf, 'iscsi_info', [])
return
for node in nodes or []:
if 'Name' not in node.attrib:
msg = _('Name must be specified for initiator.')
LOG.error(msg)
raise exception.InvalidInput(msg)
iscsi_info = []
for node in nodes:
props = {}
for item in node.items():
props[item[0].strip()] = item[1].strip()
iscsi_info.append(props)
initiators[node.attrib['Name']] = node.attrib
iscsi_info['initiators'] = initiators
setattr(self.conf, 'iscsi_info', iscsi_info)
def _parse_rmt_iscsi_info(self, iscsi_info):
if not (iscsi_info and iscsi_info.strip()):
return []
# Consider iscsi_info value:
# ' {Name:xxx ;;TargetPortGroup: xxx};\n'
# '{Name:\t\rxxx;CHAPinfo: mm-usr#mm-pwd} '
# Step 1, ignore whitespace characters, convert to:
# '{Name:xxx;;TargetPortGroup:xxx};{Name:xxx;CHAPinfo:mm-usr#mm-pwd}'
iscsi_info = ''.join(iscsi_info.split())
# Step 2, make initiators configure list, convert to:
# ['Name:xxx;;TargetPortGroup:xxx', 'Name:xxx;CHAPinfo:mm-usr#mm-pwd']
initiator_infos = iscsi_info[1:-1].split('};{')
# Step 3, get initiator configure pairs, convert to:
# [['Name:xxx', '', 'TargetPortGroup:xxx'],
# ['Name:xxx', 'CHAPinfo:mm-usr#mm-pwd']]
initiator_infos = map(lambda x: x.split(';'), initiator_infos)
# Step 4, remove invalid configure pairs, convert to:
# [['Name:xxx', 'TargetPortGroup:xxx'],
# ['Name:xxx', 'CHAPinfo:mm-usr#mm-pwd']]
initiator_infos = map(lambda x: [y for y in x if y],
initiator_infos)
# Step 5, make initiators configure dict, convert to:
# [{'TargetPortGroup': 'xxx', 'Name': 'xxx'},
# {'Name': 'xxx', 'CHAPinfo': 'mm-usr#mm-pwd'}]
get_opts = lambda x: x.split(':', 1)
initiator_infos = map(lambda x: dict(map(get_opts, x)),
initiator_infos)
# Convert generator to list for py3 compatibility.
initiator_infos = list(initiator_infos)
# Step 6, replace CHAPinfo 'user#pwd' to 'user;pwd'
key = 'CHAPinfo'
for info in initiator_infos:
if key in info:
info[key] = info[key].replace('#', ';', 1)
return initiator_infos
def get_replication_devices(self):
devs = self.conf.safe_get('replication_device')
if not devs:
return []
devs_config = []
for dev in devs:
dev_config = {}
dev_config['backend_id'] = dev['backend_id']
dev_config['san_address'] = dev['san_address'].split(';')
dev_config['san_user'] = dev['san_user']
dev_config['san_password'] = dev['san_password']
dev_config['storage_pools'] = dev['storage_pool'].split(';')
dev_config['iscsi_info'] = self._parse_rmt_iscsi_info(
dev.get('iscsi_info'))
dev_config['iscsi_default_target_ip'] = (
dev['iscsi_default_target_ip'].split(';')
if 'iscsi_default_target_ip' in dev
else [])
devs_config.append(dev_config)
return devs_config
def get_local_device(self):
dev_config = {
'backend_id': "default",
'san_address': self.conf.san_address,
'san_user': self.conf.san_user,
'san_password': self.conf.san_password,
'storage_pools': self.conf.storage_pools,
'iscsi_info': self.conf.iscsi_info,
'iscsi_default_target_ip': self.conf.iscsi_default_target_ip,
def _fc_info(self, xml_root):
fc_info = {
'ALUA': xml_root.findtext('FC/ALUA'),
'FAILOVERMODE': xml_root.findtext('FC/FAILOVERMODE'),
'SPECIALMODETYPE': xml_root.findtext('FC/SPECIALMODETYPE'),
'PATHTYPE': xml_root.findtext('FC/PATHTYPE'),
}
return dev_config
initiators = {}
nodes = xml_root.findall('FC/Initiator')
for node in nodes or []:
if 'Name' not in node.attrib:
msg = _('Name must be specified for initiator.')
LOG.error(msg)
raise exception.InvalidInput(msg)
initiators[node.attrib['Name']] = node.attrib
fc_info['initiators'] = initiators
setattr(self.conf, 'fc_info', fc_info)
def _parse_remote_initiator_info(self, dev, ini_type):
ini_info = {'default_target_ips': []}
if dev.get('iscsi_default_target_ip'):
ini_info['default_target_ips'] = dev[
'iscsi_default_target_ip'].split(';')
initiators = {}
if ini_type in dev:
# Analyze initiators configure text, convert to:
# [{'Name':'xxx'}, {'Name':'xxx','CHAPinfo':'mm-usr#mm-pwd'}]
ini_list = re.split('\s', dev[ini_type])
def _convert_one_iscsi_info(ini_text):
# get initiator configure attr list
attr_list = re.split('[{;}]', ini_text)
# get initiator configures
ini = {}
for attr in attr_list:
if not attr:
continue
pair = attr.split(':', 1)
if pair[0] == 'CHAPinfo':
value = pair[1].replace('#', ';', 1)
else:
value = pair[1]
ini[pair[0]] = value
if 'Name' not in ini:
msg = _('Name must be specified for initiator.')
LOG.error(msg)
raise exception.InvalidInput(msg)
return ini
for text in ini_list:
ini = _convert_one_iscsi_info(text)
initiators[ini['Name']] = ini
ini_info['initiators'] = initiators
return ini_info
def _hypermetro_devices(self, xml_root):
dev = self.conf.safe_get('hypermetro_device')
config = {}
if dev:
config = {
'san_address': dev['san_address'].split(';'),
'san_user': dev['san_user'],
'san_password': dev['san_password'],
'vstore_name': dev.get('vstore_name'),
'metro_domain': dev['metro_domain'],
'storage_pools': dev['storage_pool'].split(';')[:1],
'iscsi_info': self._parse_remote_initiator_info(
dev, 'iscsi_info'),
'fc_info': self._parse_remote_initiator_info(
dev, 'fc_info'),
}
setattr(self.conf, 'hypermetro', config)
def _replication_devices(self, xml_root):
replication_devs = self.conf.safe_get('replication_device')
config = {}
if replication_devs:
dev = replication_devs[0]
config = {
'backend_id': dev['backend_id'],
'san_address': dev['san_address'].split(';'),
'san_user': dev['san_user'],
'san_password': dev['san_password'],
'vstore_name': dev.get('vstore_name'),
'storage_pools': dev['storage_pool'].split(';')[:1],
'iscsi_info': self._parse_remote_initiator_info(
dev, 'iscsi_info'),
'fc_info': self._parse_remote_initiator_info(
dev, 'fc_info'),
}
setattr(self.conf, 'replication', config)

View File

@ -164,12 +164,9 @@ class HuaweiISCSIDriver(common.HuaweiBaseDriver, driver.ISCSIDriver):
portgroup_id = None
view_id = None
left_lunnum = -1
for ini in self.client.iscsi_info:
if ini['Name'] == initiator_name:
for key in ini:
if key == 'TargetPortGroup':
portgroup = ini['TargetPortGroup']
break
ini = self.client.iscsi_info['initiators'].get(initiator_name)
if ini and ini.get('TargetPortGroup'):
portgroup = ini['TargetPortGroup']
if portgroup:
portgroup_id = self.client.get_tgt_port_group(portgroup)

View File

@ -410,14 +410,19 @@ class ReplicaPairManager(object):
'DESCRIPTION': local_lun_info['DESCRIPTION'],
'ALLOCTYPE': local_lun_info['ALLOCTYPE'],
'CAPACITY': local_lun_info['CAPACITY'],
'WRITEPOLICY': self.conf.lun_write_type,
'PREFETCHPOLICY': self.conf.lun_prefetch_type,
'PREFETCHVALUE': self.conf.lun_prefetch_value,
'DATATRANSFERPOLICY': self.conf.lun_policy,
'READCACHEPOLICY': self.conf.lun_read_cache_policy,
'WRITECACHEPOLICY': self.conf.lun_write_cache_policy,
}
if 'WRITEPOLICY' in local_lun_info:
params['WRITEPOLICY'] = local_lun_info['WRITEPOLICY']
if 'PREFETCHPOLICY' in local_lun_info:
params['PREFETCHPOLICY'] = local_lun_info['PREFETCHPOLICY']
if 'PREFETCHVALUE' in local_lun_info:
params['PREFETCHVALUE'] = local_lun_info['PREFETCHVALUE']
if 'DATATRANSFERPOLICY' in local_lun_info:
params['DATATRANSFERPOLICY'] = local_lun_info['DATATRANSFERPOLICY']
LOG.debug('Remote lun params: %s.', params)
return params

View File

@ -44,9 +44,6 @@ class RestClient(object):
self.configuration.storage_pools)
self.iscsi_info = kwargs.get('iscsi_info',
self.configuration.iscsi_info)
self.iscsi_default_target_ip = kwargs.get(
'iscsi_default_target_ip',
self.configuration.iscsi_default_target_ip)
self.session = None
self.url = None
self.device_id = None
@ -828,29 +825,25 @@ class RestClient(object):
def find_chap_info(self, iscsi_info, initiator_name):
"""Find CHAP info from xml."""
chapinfo = None
for ini in iscsi_info:
if ini['Name'] == initiator_name:
if 'CHAPinfo' in ini:
chapinfo = ini['CHAPinfo']
break
ini = iscsi_info['initiators'].get(initiator_name)
if ini and ini.get('CHAPinfo'):
chapinfo = ini['CHAPinfo']
return chapinfo
def _find_alua_info(self, iscsi_info, initiator_name):
"""Find ALUA info from xml."""
multipath_type = 0
for ini in iscsi_info:
if ini['Name'] == initiator_name:
if 'ALUA' in ini:
if ini['ALUA'] != '1' and ini['ALUA'] != '0':
msg = (_(
'Invalid ALUA value. '
'ALUA value must be 1 or 0.'))
LOG.error(msg)
raise exception.InvalidInput(msg)
else:
multipath_type = ini['ALUA']
break
ini = iscsi_info['initiators'].get(initiator_name)
if ini and ini.get('ALUA'):
if ini['ALUA'] != '1' and ini['ALUA'] != '0':
msg = (_(
'Invalid ALUA value. '
'ALUA value must be 1 or 0.'))
LOG.error(msg)
raise exception.InvalidInput(msg)
else:
multipath_type = ini['ALUA']
return multipath_type
def _use_chap(self, chapinfo, initiator_name, host_id):
@ -1297,9 +1290,10 @@ class RestClient(object):
portgroup_id = None
if multipath:
for ini in self.iscsi_info:
if ini['Name'] == initiator:
portgroup = ini.get('TargetPortGroup')
ini = self.iscsi_info['initiators'].get(initiator)
if ini and ini.get('TargetPortGroup'):
portgroup = ini['TargetPortGroup']
if portgroup:
portgroup_id = self.get_tgt_port_group(portgroup)
temp_tgt_ips = self._get_tgt_ip_from_portgroup(portgroup_id)
@ -1340,14 +1334,13 @@ class RestClient(object):
def _get_target_ip(self, initiator):
target_ips = []
for ini in self.iscsi_info:
if ini['Name'] == initiator:
if ini.get('TargetIP'):
target_ips.append(ini.get('TargetIP'))
ini = self.iscsi_info['initiators'].get(initiator)
if ini and ini.get('TargetIP'):
target_ips.append(ini['TargetIP'])
# If not specify target IP for some initiators, use default IP.
if not target_ips:
default_target_ips = self.iscsi_default_target_ip
default_target_ips = self.iscsi_info['default_target_ips']
if default_target_ips:
target_ips.append(default_target_ips[0])