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:
<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

(cherry picked from I36fd52b97f790f1c68eaf24b6c12e7ef5d16145d)

Change-Id: I6792aaae050b126088447a0df65e10c425b84102
This commit is contained in:
zhangchao010 2013-10-14 12:47:44 +08:00
parent bdb5d982d0
commit 0d9f2594fd
9 changed files with 320 additions and 97 deletions

View 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):

View 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')

View 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

View 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()

View 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."""

View 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()

View File

@ -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

View 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,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."""

View 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')
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: