diff --git a/cinder/tests/test_huawei_hvs.py b/cinder/tests/test_huawei_hvs.py index c2ed9d01cfa..c05b91f9568 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 209366f6e16..4657f0375a1 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 3ee2d1834eb..f4d1b8a9827 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 ed87051fa9f..ab9d75319a4 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 9883c84e920..513f733c981 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 7a6f7914504..478b9a11489 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 00000000000..38a3383842c --- /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 e8a802fb157..edb4689862a 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,23 +1231,34 @@ 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') + 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 hvsurl) or (not username) or (not pwd): - err_msg = (_('_check_conf_file: Config file invalid. HVSURL,' - ' UserName and UserPassword 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) - if not pool_node: - 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.""" diff --git a/cinder/volume/drivers/huawei/ssh_common.py b/cinder/volume/drivers/huawei/ssh_common.py index f83575053f0..ec30edd36fb 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') + 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) - 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.')) + # 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: