From b30402eeeddefb855126a32fee15db164684ba8c Mon Sep 17 00:00:00 2001 From: zhangchao010 Date: Mon, 14 Oct 2013 12:47:44 +0800 Subject: [PATCH] Fix Huawei drivers to support other host OSs Huawei drivers create Linux hosts by default when attaching volumes. This patch makes them also support Windows, XenServer, AIX, etc. The default OS is still Linux if it is not specified. Users need to configure the host OS types in Huawei XML configuration file. They need to set the items like this: When attaching a volume, the driver will get the host IP from nova. We compare that IP with the IP in "HostIP" to get the corresponding OS type. Closes-bug: #1229759 Change-Id: I36fd52b97f790f1c68eaf24b6c12e7ef5d16145d --- cinder/tests/test_huawei_hvs.py | 3 +- cinder/tests/test_huawei_t_dorado.py | 72 +++++++++- cinder/volume/drivers/huawei/__init__.py | 4 +- cinder/volume/drivers/huawei/huawei_dorado.py | 2 +- cinder/volume/drivers/huawei/huawei_hvs.py | 2 +- cinder/volume/drivers/huawei/huawei_t.py | 7 +- cinder/volume/drivers/huawei/huawei_utils.py | 135 ++++++++++++++++++ cinder/volume/drivers/huawei/rest_common.py | 85 +++++------ cinder/volume/drivers/huawei/ssh_common.py | 111 +++++++------- 9 files changed, 322 insertions(+), 99 deletions(-) create mode 100644 cinder/volume/drivers/huawei/huawei_utils.py diff --git a/cinder/tests/test_huawei_hvs.py b/cinder/tests/test_huawei_hvs.py index c2ed9d01c..c05b91f95 100644 --- a/cinder/tests/test_huawei_hvs.py +++ b/cinder/tests/test_huawei_hvs.py @@ -61,7 +61,8 @@ test_snap = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635', FakeConnector = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3', 'wwpns': ['10000090fa0d6754'], 'wwnns': ['10000090fa0d6755'], - 'host': 'fakehost'} + 'host': 'fakehost', + 'ip': '10.10.0.1'} def Fake_sleep(time): diff --git a/cinder/tests/test_huawei_t_dorado.py b/cinder/tests/test_huawei_t_dorado.py index 209366f6e..4657f0375 100644 --- a/cinder/tests/test_huawei_t_dorado.py +++ b/cinder/tests/test_huawei_t_dorado.py @@ -34,6 +34,7 @@ from cinder import exception from cinder import test from cinder import utils from cinder.volume import configuration as conf +from cinder.volume.drivers.huawei import huawei_utils from cinder.volume.drivers.huawei import HuaweiVolumeDriver from cinder.volume.drivers.huawei import ssh_common from cinder.volume import volume_types @@ -150,7 +151,8 @@ FAKE_SNAPSHOT = {'name': 'keke34fe-223f-dd33-4423-asdfghjklqwf', FAKE_CONNECTOR = {'initiator': 'iqn.1993-08.debian:01:ec2bff7ac3a3', 'wwpns': ['1000000164s45126'], 'wwnns': ['2000666666666565'], - 'host': 'fakehost'} + 'host': 'fakehost', + 'ip': '10.10.0.1'} RESPOOL_A_SIM = {'Size': '10240', 'Valid Size': '5120'} RESPOOL_B_SIM = {'Size': '10240', 'Valid Size': '10240'} @@ -300,6 +302,11 @@ def create_fake_conf_file(filename): initiator.setAttribute('TargetIP', '192.168.100.2') iscsi.appendChild(initiator) + os_type = doc.createElement('Host') + os_type.setAttribute('OSType', 'Linux') + os_type.setAttribute('HostIP', '10.10.0.1') + config.appendChild(os_type) + tmp_file = open(filename, 'w') tmp_file.write(doc.toprettyxml(indent='')) tmp_file.close() @@ -1080,6 +1087,13 @@ class HuaweiTISCSIDriverTestCase(test.TestCase): self.assertRaises(exception.InvalidInput, tmp_driver.create_volume, FAKE_VOLUME) modify_conf(self.fake_conf_file, 'LUN/LUNType', 'Thick') + # Test OSType invalid + modify_conf(self.fake_conf_file, 'Host', 'invalid_type', + attrib='OSType') + tmp_driver = HuaweiVolumeDriver(configuration=self.configuration) + self.assertRaises(exception.InvalidInput, + tmp_driver.do_setup, None) + modify_conf(self.fake_conf_file, 'Host', 'Linux', attrib='OSType') # Test TargetIP not found modify_conf(self.fake_conf_file, 'iSCSI/DefaultTargetIP', '') modify_conf(self.fake_conf_file, 'iSCSI/Initiator', '', attrib='Name') @@ -1643,3 +1657,59 @@ class SSHMethodTestCase(test.TestCase): def _fake_recv2(self, nBytes): raise socket.timeout() + + +class HuaweiUtilsTestCase(test.TestCase): + def __init__(self, *args, **kwargs): + super(HuaweiUtilsTestCase, self).__init__(*args, **kwargs) + + def setUp(self): + super(HuaweiUtilsTestCase, self).setUp() + + self.tmp_dir = tempfile.mkdtemp() + self.fake_conf_file = self.tmp_dir + '/cinder_huawei_conf.xml' + create_fake_conf_file(self.fake_conf_file) + + def tearDown(self): + if os.path.exists(self.fake_conf_file): + os.remove(self.fake_conf_file) + shutil.rmtree(self.tmp_dir) + super(HuaweiUtilsTestCase, self).tearDown() + + def test_parse_xml_file_ioerror(self): + tmp_fonf_file = '/xxx/cinder_huawei_conf.xml' + self.assertRaises(IOError, huawei_utils.parse_xml_file, tmp_fonf_file) + + def test_is_xml_item_exist(self): + root = huawei_utils.parse_xml_file(self.fake_conf_file) + res = huawei_utils.is_xml_item_exist(root, 'Storage/UserName') + self.assertTrue(res) + res = huawei_utils.is_xml_item_exist(root, 'xxx') + self.assertFalse(res) + res = huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool', 'Name') + self.assertTrue(res) + res = huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool', 'xxx') + self.assertFalse(res) + + def test_is_xml_item_valid(self): + root = huawei_utils.parse_xml_file(self.fake_conf_file) + res = huawei_utils.is_xml_item_valid(root, 'LUN/LUNType', + ['Thin', 'Thick']) + self.assertTrue(res) + res = huawei_utils.is_xml_item_valid(root, 'LUN/LUNType', ['test']) + self.assertFalse(res) + res = huawei_utils.is_xml_item_valid(root, 'Host', + ['Linux', 'Windows'], 'OSType') + self.assertTrue(res) + res = huawei_utils.is_xml_item_valid(root, 'Host', ['test'], 'OSType') + self.assertFalse(res) + + def test_get_conf_host_os_type(self): + # Default os is Linux + res = huawei_utils.get_conf_host_os_type('10.10.10.1', + self.fake_conf_file) + self.assertEqual(res, '0') + modify_conf(self.fake_conf_file, 'Host', 'Windows', 'OSType') + res = huawei_utils.get_conf_host_os_type(FAKE_CONNECTOR['ip'], + self.fake_conf_file) + self.assertEqual(res, '1') diff --git a/cinder/volume/drivers/huawei/__init__.py b/cinder/volume/drivers/huawei/__init__.py index 3ee2d1834..f4d1b8a98 100644 --- a/cinder/volume/drivers/huawei/__init__.py +++ b/cinder/volume/drivers/huawei/__init__.py @@ -30,7 +30,7 @@ from cinder.volume import driver from cinder.volume.drivers.huawei import huawei_dorado from cinder.volume.drivers.huawei import huawei_hvs from cinder.volume.drivers.huawei import huawei_t -from cinder.volume.drivers.huawei import ssh_common +from cinder.volume.drivers.huawei import huawei_utils LOG = logging.getLogger(__name__) @@ -78,7 +78,7 @@ class HuaweiVolumeDriver(object): def _get_conf_info(self, filename): """Get product type and connection protocol from config file.""" - root = ssh_common.parse_xml_file(filename) + root = huawei_utils.parse_xml_file(filename) product = root.findtext('Storage/Product').strip() protocol = root.findtext('Storage/Protocol').strip() if (product in self._product.keys() and diff --git a/cinder/volume/drivers/huawei/huawei_dorado.py b/cinder/volume/drivers/huawei/huawei_dorado.py index ed87051fa..ab9d75319 100644 --- a/cinder/volume/drivers/huawei/huawei_dorado.py +++ b/cinder/volume/drivers/huawei/huawei_dorado.py @@ -81,7 +81,7 @@ class HuaweiDoradoFCDriver(huawei_t.HuaweiTFCDriver): self.common._update_login_info() # First, add a host if it is not added before. - host_id = self.common.add_host(connector['host']) + host_id = self.common.add_host(connector['host'], connector['ip']) # Then, add free FC ports to the host. ini_wwns = connector['wwpns'] free_wwns = self._get_connected_free_wwns() diff --git a/cinder/volume/drivers/huawei/huawei_hvs.py b/cinder/volume/drivers/huawei/huawei_hvs.py index 9883c84e9..513f733c9 100644 --- a/cinder/volume/drivers/huawei/huawei_hvs.py +++ b/cinder/volume/drivers/huawei/huawei_hvs.py @@ -34,11 +34,11 @@ class HuaweiHVSISCSIDriver(driver.ISCSIDriver): def do_setup(self, context): """Instantiate common class and log in storage system.""" self.common = HVSCommon(configuration=self.configuration) - self.common.login() def check_for_setup_error(self): """Check configuration file.""" self.common._check_conf_file() + self.common.login() def create_volume(self, volume): """Create a volume.""" diff --git a/cinder/volume/drivers/huawei/huawei_t.py b/cinder/volume/drivers/huawei/huawei_t.py index 7a6f79145..478b9a114 100644 --- a/cinder/volume/drivers/huawei/huawei_t.py +++ b/cinder/volume/drivers/huawei/huawei_t.py @@ -25,6 +25,7 @@ import time from cinder import exception from cinder.openstack.common import log as logging from cinder.volume import driver +from cinder.volume.drivers.huawei import huawei_utils from cinder.volume.drivers.huawei import ssh_common @@ -106,7 +107,7 @@ class HuaweiTISCSIDriver(driver.ISCSIDriver): self._get_iscsi_params(connector['initiator']) # First, add a host if not added before. - host_id = self.common.add_host(connector['host'], + host_id = self.common.add_host(connector['host'], connector['ip'], connector['initiator']) # Then, add the iSCSI port to the host. @@ -175,7 +176,7 @@ class HuaweiTISCSIDriver(driver.ISCSIDriver): """ iscsiinfo = {} - root = ssh_common.parse_xml_file(filename) + root = huawei_utils.parse_xml_file(filename) default_ip = root.findtext('iSCSI/DefaultTargetIP') if default_ip: @@ -441,7 +442,7 @@ class HuaweiTFCDriver(driver.FibreChannelDriver): self.common._update_login_info() # First, add a host if it is not added before. - host_id = self.common.add_host(connector['host']) + host_id = self.common.add_host(connector['host'], connector['ip']) # Then, add free FC ports to the host. ini_wwns = connector['wwpns'] free_wwns = self._get_connected_free_wwns() diff --git a/cinder/volume/drivers/huawei/huawei_utils.py b/cinder/volume/drivers/huawei/huawei_utils.py new file mode 100644 index 000000000..38a338384 --- /dev/null +++ b/cinder/volume/drivers/huawei/huawei_utils.py @@ -0,0 +1,135 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Huawei Technologies Co., Ltd. +# Copyright (c) 2012 OpenStack LLC. +# 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 xml.etree import ElementTree as ET + +from cinder import exception +from cinder.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + +os_type = {'Linux': '0', + 'Windows': '1', + 'Solaris': '2', + 'HP-UX': '3', + 'AIX': '4', + 'XenServer': '5', + 'Mac OS X': '6', + 'VMware ESX': '7'} + + +def parse_xml_file(filepath): + """Get root of xml file.""" + try: + tree = ET.parse(filepath) + root = tree.getroot() + return root + except IOError as err: + LOG.error(_('parse_xml_file: %s') % err) + raise err + + +def get_xml_item(xml_root, item): + """Get the given item details. + + :param xml_root: The root of xml tree + :param item: The tag need to get + :return: A dict contains all the config info of the given item. + """ + items_list = [] + items = xml_root.findall(item) + for item in items: + tmp_dict = {'text': None, 'attrib': {}} + if item.text: + tmp_dict['text'] = item.text.strip() + for key, val in item.attrib.items(): + if val: + item.attrib[key] = val.strip() + tmp_dict['attrib'] = item.attrib + items_list.append(tmp_dict) + return items_list + + +def is_xml_item_exist(xml_root, item, attrib_key=None): + """Check if the given item exits in xml config file. + + :param xml_root: The root of xml tree + :param item: The xml tag to check + :param attrib_key: The xml attrib to check + :return: True of False + """ + items_list = get_xml_item(xml_root, item) + value = [] + if attrib_key: + for tmp_dict in items_list: + if tmp_dict['attrib'].get(attrib_key, None): + return True + else: + if items_list and items_list[0]['text']: + return True + return False + + +def is_xml_item_valid(xml_root, item, valid_list, attrib_key=None): + """Check if the given item is valid in xml config file. + + :param xml_root: The root of xml tree + :param item: The xml tag to check + :param valid_list: The valid item value + :param attrib_key: The xml attrib to check + :return: True of False + """ + items_list = get_xml_item(xml_root, item) + if attrib_key: + for tmp_dict in items_list: + value = tmp_dict['attrib'].get(attrib_key, None) + if value not in valid_list: + return False + else: + value = items_list[0]['text'] + if value not in valid_list: + return False + + return True + + +def get_conf_host_os_type(host_ip, config): + """Get host OS type from xml config file. + + :param host_ip: The IP of Nova host + :param config: xml config file + :return: host OS type + """ + os_conf = {} + root = parse_xml_file(config) + hosts_list = get_xml_item(root, 'Host') + for host in hosts_list: + os = host['attrib']['OSType'].strip() + ips = [ip.strip() for ip in host['attrib']['HostIP'].split(',')] + os_conf[os] = ips + host_os = None + for k, v in os_conf.items(): + if host_ip in v: + host_os = os_type.get(k, None) + if not host_os: + host_os = os_type['Linux'] # default os type + + LOG.debug(_('_get_host_os_type: Host %(ip)s OS type is %(os)s.') + % {'ip': host_ip, 'os': host_os}) + + return host_os diff --git a/cinder/volume/drivers/huawei/rest_common.py b/cinder/volume/drivers/huawei/rest_common.py index e8a802fb1..edb468986 100644 --- a/cinder/volume/drivers/huawei/rest_common.py +++ b/cinder/volume/drivers/huawei/rest_common.py @@ -32,6 +32,7 @@ from cinder.openstack.common import excutils from cinder.openstack.common import log as logging from cinder import units from cinder import utils +from cinder.volume.drivers.huawei import huawei_utils from cinder.volume import volume_types @@ -48,6 +49,7 @@ class HVSCommon(): self.configuration = configuration self.cookie = cookielib.CookieJar() self.url = None + self.xml_conf = self.configuration.cinder_huawei_conf_file def call(self, url=False, data=None, method=None): """Send requests to HVS server. @@ -170,9 +172,9 @@ class HVSCommon(): def _assert_data_in_result(self, result, msg): if "data" not in result: - msg = _('%s "data" was not in result.') % msg - LOG.error(msg) - raise exception.CinderException(msg) + err_msg = _('%s "data" was not in result.') % msg + LOG.error(err_msg) + raise exception.CinderException(err_msg) def _create_volume(self, lun_param): url = self.url + "/lun" @@ -266,17 +268,6 @@ class HVSCommon(): result = self.call(url, data, "DELETE") self._assert_rest_result(result, 'delete lun error') - def _read_xml(self): - """Open xml file and parse the content.""" - filename = self.configuration.cinder_huawei_conf_file - try: - tree = ET.parse(filename) - root = tree.getroot() - except Exception as err: - LOG.error(_('_read_xml:%s') % err) - raise err - return root - def _encode_name(self, name): uuid_str = name.replace("-", "") vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str) @@ -285,7 +276,7 @@ class HVSCommon(): return newuuid def _find_pool_info(self): - root = self._read_xml() + root = huawei_utils.parse_xml_file(self.xml_conf) pool_name = root.findtext('LUN/StoragePool') if not pool_name: err_msg = _("Invalid resource pool: %s") % pool_name @@ -455,7 +446,7 @@ class HVSCommon(): return result['data']['ID'] - def _add_host_into_hostgroup(self, host_name): + def _add_host_into_hostgroup(self, host_name, host_ip): """Associate host to hostgroup. If host group doesn't exist, create one. @@ -468,7 +459,9 @@ class HVSCommon(): hostid = self._find_host(host_name) if hostid is None: - hostid = self._add_host(host_name) + os_type = huawei_utils.get_conf_host_os_type(host_ip, + self.xml_conf) + hostid = self._add_host(host_name, os_type) self._associate_host_to_hostgroup(hostgroup_id, hostid) return hostid, hostgroup_id @@ -515,17 +508,18 @@ class HVSCommon(): def initialize_connection_iscsi(self, volume, connector): """Map a volume to a host and return target iSCSI information.""" initiator_name = connector['initiator'] - host_name = connector['host'] volume_name = self._encode_name(volume['id']) LOG.debug(_('initiator name:%(initiator_name)s, ' 'volume name:%(volume)s.') % {'initiator_name': initiator_name, 'volume': volume_name}) + (iscsi_iqn, target_ip) = self._get_iscsi_params(connector) #create host_goup if not exist - hostid, hostgroup_id = self._add_host_into_hostgroup(host_name) + hostid, hostgroup_id = self._add_host_into_hostgroup(connector['host'], + connector['ip']) self._ensure_initiator_added(initiator_name, hostid) # Mapping lungooup and hostgoup to view @@ -546,7 +540,6 @@ class HVSCommon(): def initialize_connection_fc(self, volume, connector): wwns = connector['wwpns'] - host_name = connector['host'] volume_name = self._encode_name(volume['id']) LOG.debug(_('initiator name:%(initiator_name)s, ' @@ -554,8 +547,9 @@ class HVSCommon(): % {'initiator_name': wwns, 'volume': volume_name}) - # Create host goup if not exist - hostid, hostgroup_id = self._add_host_into_hostgroup(host_name) + # Create host group if not exist + hostid, hostgroup_id = self._add_host_into_hostgroup(connector['host'], + connector['ip']) free_wwns = self._get_connected_free_wwns() LOG.debug(_("the free wwns %s") % free_wwns) @@ -713,12 +707,12 @@ class HVSCommon(): break return host_id - def _add_host(self, hostname): + def _add_host(self, hostname, type): """Add a new host.""" url = self.url + "/host" data = json.dumps({"TYPE": "21", "NAME": hostname, - "OPERATIONSYSTEM": "0"}) + "OPERATIONSYSTEM": type}) result = self.call(url, data) self._assert_rest_result(result, 'Add new host error.') @@ -931,7 +925,7 @@ class HVSCommon(): 'PrefetchValue': '0', 'PrefetchTimes': '0'} - root = self._read_xml() + root = huawei_utils.parse_xml_file(self.xml_conf) luntype = root.findtext('LUN/LUNType') if luntype: if luntype.strip() in ['Thick', 'Thin']: @@ -1065,7 +1059,7 @@ class HVSCommon(): def _get_iscsi_conf(self): """Get iSCSI info from config file.""" iscsiinfo = {} - root = self._read_xml() + root = huawei_utils.parse_xml_file(self.xml_conf) iscsiinfo['DefaultTargetIP'] = \ root.findtext('iSCSI/DefaultTargetIP').strip() initiator_list = [] @@ -1237,24 +1231,35 @@ class HVSCommon(): def _check_conf_file(self): """Check the config file, make sure the essential items are set.""" - root = self._read_xml() - hvsurl = root.findtext('Storage/HVSURL') - username = root.findtext('Storage/UserName') - pwd = root.findtext('Storage/UserPassword') - pool_node = root.findall('LUN/StoragePool') - - if (not hvsurl) or (not username) or (not pwd): - err_msg = (_('_check_conf_file: Config file invalid. HVSURL,' - ' UserName and UserPassword must be set.')) - LOG.error(err_msg) - raise exception.InvalidInput(reason=err_msg) + root = huawei_utils.parse_xml_file(self.xml_conf) + check_list = ['Storage/HVSURL', 'Storage/UserName', + 'Storage/UserPassword'] + for item in check_list: + if not huawei_utils.is_xml_item_exist(root, item): + err_msg = (_('_check_conf_file: Config file invalid. ' + '%s must be set.') % item) + LOG.error(err_msg) + raise exception.InvalidInput(reason=err_msg) - if not pool_node: - err_msg = (_('_check_conf_file: Config file invalid. ' - 'StoragePool must be set.')) + # make sure storage pool is set + if not huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool'): + err_msg = _('_check_conf_file: Config file invalid. ' + 'StoragePool must be set.') LOG.error(err_msg) raise exception.InvalidInput(reason=err_msg) + # make sure host os type valid + if huawei_utils.is_xml_item_exist(root, 'Host', 'OSType'): + os_list = huawei_utils.os_type.keys() + if not huawei_utils.is_xml_item_valid(root, 'Host', os_list, + 'OSType'): + err_msg = (_('_check_conf_file: Config file invalid. ' + 'Host OSType invalid.\n' + 'The valid values are: %(os_list)s') + % {'os_list': os_list}) + LOG.error(err_msg) + raise exception.InvalidInput(reason=err_msg) + def _get_iscsi_params(self, connector): """Get target iSCSI params, including iqn, IP.""" initiator = connector['initiator'] diff --git a/cinder/volume/drivers/huawei/ssh_common.py b/cinder/volume/drivers/huawei/ssh_common.py index f83575053..ec30edd36 100644 --- a/cinder/volume/drivers/huawei/ssh_common.py +++ b/cinder/volume/drivers/huawei/ssh_common.py @@ -34,6 +34,7 @@ from cinder import exception from cinder.openstack.common import excutils from cinder.openstack.common import log as logging from cinder import utils +from cinder.volume.drivers.huawei import huawei_utils from cinder.volume import volume_types @@ -44,17 +45,6 @@ HOST_NAME_PREFIX = 'Host_' VOL_AND_SNAP_NAME_PREFIX = 'OpenStack_' -def parse_xml_file(filepath): - """Get root of xml file.""" - try: - tree = ET.parse(filepath) - root = tree.getroot() - return root - except IOError as err: - LOG.error(_('parse_xml_file: %s') % err) - raise err - - def ssh_read(user, channel, cmd, timeout): """Get results of CLI commands.""" result = '' @@ -105,7 +95,7 @@ class TseriesCommon(): self.hostgroup_id = None self.ssh_pool = None self.lock_ip = threading.Lock() - self.luncopy_list = [] # to storage LUNCopy name + self.luncopy_list = [] # to store LUNCopy name def do_setup(self, context): """Check config file.""" @@ -119,27 +109,34 @@ class TseriesCommon(): def _check_conf_file(self): """Check config file, make sure essential items are set.""" - root = parse_xml_file(self.xml_conf) - IP1 = root.findtext('Storage/ControllerIP0') - IP2 = root.findtext('Storage/ControllerIP1') - username = root.findtext('Storage/UserName') - pwd = root.findtext('Storage/UserPassword') - pool_node = root.findall('LUN/StoragePool') - - if (not IP1 or not IP2) or (not username) or (not pwd): - err_msg = (_('_check_conf_file: Config file invalid. Controler IP,' - ' UserName and UserPassword must be set.')) + root = huawei_utils.parse_xml_file(self.xml_conf) + check_list = ['Storage/ControllerIP0', 'Storage/ControllerIP1', + 'Storage/UserName', 'Storage/UserPassword'] + for item in check_list: + if not huawei_utils.is_xml_item_exist(root, item): + err_msg = (_('_check_conf_file: Config file invalid. ' + '%s must be set.') % item) + LOG.error(err_msg) + raise exception.InvalidInput(reason=err_msg) + + # make sure storage pool is set + if not huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool', 'Name'): + err_msg = _('_check_conf_file: Config file invalid. ' + 'StoragePool must be set.') LOG.error(err_msg) raise exception.InvalidInput(reason=err_msg) - for pool in pool_node: - if pool.attrib['Name']: - return - # If pool_node is None or pool.attrib['Name'] is None. - err_msg = (_('_check_conf_file: Config file invalid. ' - 'StoragePool must be set.')) - LOG.error(err_msg) - raise exception.InvalidInput(reason=err_msg) + # If setting os type, make sure it valid + if huawei_utils.is_xml_item_exist(root, 'Host', 'OSType'): + os_list = huawei_utils.os_type.keys() + if not huawei_utils.is_xml_item_valid(root, 'Host', os_list, + 'OSType'): + err_msg = (_('_check_conf_file: Config file invalid. ' + 'Host OSType is invalid.\n' + 'The valid values are: %(os_list)s') + % {'os_list': os_list}) + LOG.error(err_msg) + raise exception.InvalidInput(reason=err_msg) def _get_login_info(self): """Get login IP, username and password from config file.""" @@ -356,7 +353,7 @@ class TseriesCommon(): 'PrefetchTimes': '0', 'StoragePool': []} - root = parse_xml_file(self.xml_conf) + root = huawei_utils.parse_xml_file(self.xml_conf) luntype = root.findtext('LUN/LUNType') if luntype: @@ -405,7 +402,7 @@ class TseriesCommon(): maxpool_id = None maxpool_size = 0.0 nameindex, sizeindex = ((1, 4) if luntype == 'Thin' else (5, 3)) - pools_dev = sorted(pools_dev, key=lambda x: int(x[sizeindex])) + pools_dev = sorted(pools_dev, key=lambda x: float(x[sizeindex])) while len(pools_dev) > 0: pool = pools_dev.pop() if pool[nameindex] in pools_conf: @@ -892,7 +889,7 @@ class TseriesCommon(): return hostlun_id - def add_host(self, host_name, initiator=None): + def add_host(self, host_name, host_ip, initiator=None): """Create a host and add it to hostgroup.""" # Create an OpenStack hostgroup if not created before. hostgroup_name = HOST_GROUP_NAME @@ -913,7 +910,9 @@ class TseriesCommon(): host_name = HOST_NAME_PREFIX + host_name host_id = self._get_host_id(host_name, self.hostgroup_id) if host_id is None: - self._create_host(host_name, self.hostgroup_id) + os_type = huawei_utils.get_conf_host_os_type(host_ip, + self.xml_conf) + self._create_host(host_name, self.hostgroup_id, os_type) host_id = self._get_host_id(host_name, self.hostgroup_id) return host_id @@ -955,11 +954,12 @@ class TseriesCommon(): return tmp_line[0] return None - def _create_host(self, hostname, hostgroupid): + def _create_host(self, hostname, hostgroupid, type): """Run CLI command to add host.""" - cli_cmd = ('addhost -group %(groupid)s -n %(hostname)s -t 0' + cli_cmd = ('addhost -group %(groupid)s -n %(hostname)s -t %(type)s' % {'groupid': hostgroupid, - 'hostname': hostname}) + 'hostname': hostname, + 'type': type}) out = self._execute_cli(cli_cmd) self._assert_cli_operate_out('_create_host', @@ -1178,30 +1178,41 @@ class DoradoCommon(TseriesCommon): def _check_conf_file(self): """Check the config file, make sure the key elements are set.""" - root = parse_xml_file(self.xml_conf) + root = huawei_utils.parse_xml_file(self.xml_conf) # Check login infomation - IP1 = root.findtext('Storage/ControllerIP0') - IP2 = root.findtext('Storage/ControllerIP1') - username = root.findtext('Storage/UserName') - pwd = root.findtext('Storage/UserPassword') - if (not IP1 and not IP2) or (not username) or (not pwd): - err_msg = (_('Config file invalid. Controler IP, UserName, ' - 'UserPassword must be specified.')) - LOG.error(err_msg) - raise exception.InvalidInput(reason=err_msg) + check_list = ['Storage/ControllerIP0', 'Storage/ControllerIP1', + 'Storage/UserName', 'Storage/UserPassword'] + for item in check_list: + if not huawei_utils.is_xml_item_exist(root, item): + err_msg = (_('_check_conf_file: Config file invalid. ' + '%s must be set.') % item) + LOG.error(err_msg) + raise exception.InvalidInput(reason=err_msg) # Check storage pool # No need for Dorado2100 G2 self.login_info = self._get_login_info() self.device_type = self._get_device_type() if self.device_type == 'Dorado5100': - pool_node = root.findall('LUN/StoragePool') - if not pool_node: + if not huawei_utils.is_xml_item_exist(root, 'LUN/StoragePool', + 'Name'): err_msg = (_('_check_conf_file: Config file invalid. ' 'StoragePool must be specified.')) LOG.error(err_msg) raise exception.InvalidInput(reason=err_msg) + # If setting os type, make sure it valid + if huawei_utils.is_xml_item_exist(root, 'Host', 'OSType'): + os_list = huawei_utils.os_type.keys() + if not huawei_utils.is_xml_item_valid(root, 'Host', os_list, + 'OSType'): + err_msg = (_('_check_conf_file: Config file invalid. ' + 'Host OSType is invalid.\n' + 'The valid values are: %(os_list)s') + % {'os_list': os_list}) + LOG.error(err_msg) + raise exception.InvalidInput(reason=err_msg) + def _get_device_type(self): """Run CLI command to get system type.""" cli_cmd = 'showsys' @@ -1333,7 +1344,7 @@ class DoradoCommon(TseriesCommon): 'WriteType': '1', 'MirrorSwitch': '1'} - root = parse_xml_file(self.xml_conf) + root = huawei_utils.parse_xml_file(self.xml_conf) luntype = root.findtext('LUN/LUNType') if luntype: -- 2.45.2