]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Fix Huawei drivers to support other host OSs
authorzhangchao010 <zhangchao010@huawei.com>
Mon, 14 Oct 2013 04:47:44 +0000 (12:47 +0800)
committerzhangchao010 <zhangchao010@huawei.com>
Mon, 14 Oct 2013 04:47:44 +0000 (12:47 +0800)
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:
<Host OSType="Windows" HostIP="10.10.0.1, 10.10.0.2, ..." />
<Host .../>

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
cinder/tests/test_huawei_t_dorado.py
cinder/volume/drivers/huawei/__init__.py
cinder/volume/drivers/huawei/huawei_dorado.py
cinder/volume/drivers/huawei/huawei_hvs.py
cinder/volume/drivers/huawei/huawei_t.py
cinder/volume/drivers/huawei/huawei_utils.py [new file with mode: 0644]
cinder/volume/drivers/huawei/rest_common.py
cinder/volume/drivers/huawei/ssh_common.py

index c2ed9d01cfab366793a32ad256daee05a6581c95..c05b91f95680c813757298986a9d2823b66a90b0 100644 (file)
@@ -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):
index 209366f6e16ef231db82f3c4657bd184aa8de20b..4657f0375a14a0d7575584156a3a4956cfbb5f09 100644 (file)
@@ -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')
index 3ee2d1834ebe12b5c5189ae26a2cee42829c155b..f4d1b8a982756e8d192468fd4fbc722e84103fb3 100644 (file)
@@ -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
index ed87051fa9f697456730ce556f4cd9a085c5ec31..ab9d75319a427be3119c4c0d38b6209ff9105012 100644 (file)
@@ -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()
index 9883c84e9205ae7c18b5515260d4a39e25f9d1a7..513f733c981aa921fa7e64da1c6b5b51c11f1859 100644 (file)
@@ -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."""
index 7a6f791450413fc55fa13a6b7a5a3a38e3f38292..478b9a11489ea7ac7f3d1258c654fcd801dfe7c2 100644 (file)
@@ -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 (file)
index 0000000..38a3383
--- /dev/null
@@ -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
index e8a802fb1572f9dc4e37bcd357d88720ecf9b4aa..edb4689862a2f61c207c06984871929f85981766 100644 (file)
@@ -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']
index f83575053f019fdbf203f9c3a1c76e25b82e9cf7..ec30edd36fb86399dd4c59bd9e7c614edb6d3780 100644 (file)
@@ -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: