Merge "Huawei driver refactor(1/10)"

This commit is contained in:
Zuul 2019-01-30 02:18:03 +00:00 committed by Gerrit Code Review
commit 45f967ef9a
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])