Refactor Huawei Volume driver

This patch attempts to refactor Huawei volume driver in liberty.
We add a base driver to implement the basic functions.
The sub-class will inherit from the base driver according to different
storages.

The following changes were made in this refactor:
1. Abstract a base class named HuaweiBaseDriver to make Huawei driver more
universal. You can find it in the huawei_driver.py.
2. Put all static variables into the constants.py.
3. Rename rest_common.py to rest_client.py. rest_client.py stores the
relevant methods implemented for Huawei driver.
4. Migrate some public methods from rest_client.py to huawei_utils.py,
such as parse_xml_file(), _get_volume_type() and so on.
5. This refactor only involves structural adjustment and does not involve
functional changes.

Change-Id: I768889e2577a4d975397218eb31e89b42e08e04f
Implements: blueprint refactor-huawei-volume-driver
This commit is contained in:
chenzongliang 2015-06-04 10:43:07 +08:00 committed by Liu Xinguo
parent a5c988f93e
commit c9bb99b52f
12 changed files with 3675 additions and 3141 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -21,9 +21,9 @@ from cinder import test
CONF = cfg.CONF
HUAWEI_ISCSI_MODULE = ("cinder.volume.drivers.huawei.huawei_18000."
HUAWEI_ISCSI_MODULE = ("cinder.volume.drivers.huawei.huawei_driver."
"Huawei18000ISCSIDriver")
HUAWEI_FC_MODULE = ("cinder.volume.drivers.huawei.huawei_18000."
HUAWEI_FC_MODULE = ("cinder.volume.drivers.huawei.huawei_driver."
"Huawei18000FCDriver")
@ -47,7 +47,7 @@ class VolumeDriverCompatibility(test.TestCase):
def test_huawei_driver_iscsi_old(self):
self._load_driver(
'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSISCSIDriver')
'cinder.volume.drivers.huawei.huawei_18000.Huawei18000ISCSIDriver')
self.assertEqual(self._driver_module_name(), HUAWEI_ISCSI_MODULE)
def test_huawei_driver_iscsi_new(self):
@ -56,7 +56,7 @@ class VolumeDriverCompatibility(test.TestCase):
def test_huawei_driver_fc_old(self):
self._load_driver(
'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSFCDriver')
'cinder.volume.drivers.huawei.huawei_18000.Huawei18000FCDriver')
self.assertEqual(self._driver_module_name(), HUAWEI_FC_MODULE)
def test_huawei_driver_fc_new(self):

View File

@ -1,108 +0,0 @@
# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
# 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.
"""
Provide a unified driver class for users.
The product type and the protocol should be specified in config file before.
"""
from oslo_config import cfg
from oslo_log import log as logging
import six
from cinder import exception
from cinder.i18n import _, _LI, _LW
from cinder.volume.drivers.huawei import huawei_18000
from cinder.volume.drivers.huawei import huawei_utils
LOG = logging.getLogger(__name__)
huawei_opt = [
cfg.StrOpt('cinder_huawei_conf_file',
default='/etc/cinder/cinder_huawei_conf.xml',
help='The configuration file for the Cinder Huawei '
'driver')]
CONF = cfg.CONF
CONF.register_opts(huawei_opt)
MAPPING = {'HVS': '18000'}
class HuaweiVolumeDriver(object):
"""Define an unified driver for Huawei drivers."""
def __init__(self, *args, **kwargs):
super(HuaweiVolumeDriver, self).__init__()
self._product = {'18000': huawei_18000, 'HVS': huawei_18000}
self._protocol = {'iSCSI': 'ISCSIDriver', 'FC': 'FCDriver'}
self.driver = self._instantiate_driver(*args, **kwargs)
def _instantiate_driver(self, *args, **kwargs):
"""Instantiate the specified driver."""
self.configuration = kwargs.get('configuration', None)
if not self.configuration:
msg = (_('_instantiate_driver: configuration not found.'))
raise exception.InvalidInput(reason=msg)
self.configuration.append_config_values(huawei_opt)
conf_file = self.configuration.cinder_huawei_conf_file
(product, protocol) = self._get_conf_info(conf_file)
LOG.info(_LI('_instantiate_driver: Loading %(protocol)s driver for '
'Huawei OceanStor %(product)s series storage arrays.'),
{'protocol': protocol,
'product': product})
# Map HVS to 18000
if product in MAPPING:
LOG.warning(_LW("Product name %s is deprecated, update your "
"configuration to the new product name."),
product)
product = MAPPING[product]
driver_module = self._product[product]
driver_class = 'Huawei' + product + self._protocol[protocol]
driver_class = getattr(driver_module, driver_class)
return driver_class(*args, **kwargs)
def _get_conf_info(self, filename):
"""Get product type and connection protocol from config file."""
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
protocol in self._protocol.keys()):
return (product, protocol)
else:
msg = (_('"Product" or "Protocol" is illegal. "Product" should be '
'set to 18000. "Protocol" should be set to either iSCSI '
'or FC. Product: %(product)s Protocol: %(protocol)s')
% {'product': six.text_type(product),
'protocol': six.text_type(protocol)})
raise exception.InvalidInput(reason=msg)
def __setattr__(self, name, value):
"""Set the attribute."""
if getattr(self, 'driver', None):
self.driver.__setattr__(name, value)
return
object.__setattr__(self, name, value)
def __getattr__(self, name):
""""Get the attribute."""
drver = object.__getattribute__(self, 'driver')
return getattr(drver, name)

View File

@ -0,0 +1,41 @@
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
# 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.
STATUS_HEALTH = '1'
STATUS_RUNNING = '10'
HOSTGROUP_PREFIX = 'OpenStack_HostGroup_'
LUNGROUP_PREFIX = 'OpenStack_LunGroup_'
MAPPING_VIEW_PREFIX = 'OpenStack_Mapping_View_'
QOS_NAME_PREFIX = 'OpenStack_'
DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30
DEFAULT_WAIT_INTERVAL = 5
ERROR_CONNECT_TO_SERVER = -403
ERROR_UNAUTHORIZED_TO_SERVER = -401
MAX_HOSTNAME_LENTH = 31
OS_TYPE = {'Linux': '0',
'Windows': '1',
'Solaris': '2',
'HP-UX': '3',
'AIX': '4',
'XenServer': '5',
'Mac OS X': '6',
'VMware ESX': '7'}
HUAWEI_VALID_KEYS = ['maxIOPS', 'minIOPS', 'minBandWidth',
'maxBandWidth', 'latency', 'IOType']

View File

@ -1,199 +0,0 @@
# Copyright (c) 2013 - 2014 Huawei Technologies Co., Ltd.
# 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.
"""
Volume Drivers for Huawei OceanStor 18000 storage arrays.
"""
from cinder.volume import driver
from cinder.volume.drivers.huawei import rest_common
from cinder.zonemanager import utils as fczm_utils
class Huawei18000ISCSIDriver(driver.ISCSIDriver):
"""ISCSI driver for Huawei OceanStor 18000 storage arrays.
Version history:
1.0.0 - Initial driver
1.1.0 - Provide Huawei OceanStor 18000 storage volume driver.
"""
VERSION = "1.1.0"
def __init__(self, *args, **kwargs):
super(Huawei18000ISCSIDriver, self).__init__(*args, **kwargs)
def do_setup(self, context):
"""Instantiate common class and log in storage system."""
self.common = rest_common.RestCommon(configuration=self.configuration)
return self.common.login()
def check_for_setup_error(self):
"""Check configuration file."""
return self.common._check_conf_file()
def create_volume(self, volume):
"""Create a volume."""
lun_info = self.common.create_volume(volume)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot."""
lun_info = self.common.create_volume_from_snapshot(volume, snapshot)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def create_cloned_volume(self, volume, src_vref):
"""Create a clone of the specified volume."""
lun_info = self.common.create_cloned_volume(volume, src_vref)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def extend_volume(self, volume, new_size):
"""Extend a volume."""
return self.common.extend_volume(volume, new_size)
def delete_volume(self, volume):
"""Delete a volume."""
return self.common.delete_volume(volume)
def create_snapshot(self, snapshot):
"""Create a snapshot."""
lun_info = self.common.create_snapshot(snapshot)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def delete_snapshot(self, snapshot):
"""Delete a snapshot."""
return self.common.delete_snapshot(snapshot)
def get_volume_stats(self, refresh=False):
"""Get volume stats."""
data = self.common.update_volume_stats()
backend_name = self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = backend_name or self.__class__.__name__
data['storage_protocol'] = 'iSCSI'
data['driver_version'] = self.VERSION
return data
def initialize_connection(self, volume, connector):
"""Map a volume to a host."""
return self.common.initialize_connection_iscsi(volume, connector)
def terminate_connection(self, volume, connector, **kwargs):
"""Terminate the map."""
self.common.terminate_connection_iscsi(volume, connector)
def create_export(self, context, volume):
"""Export the volume."""
pass
def ensure_export(self, context, volume):
"""Synchronously recreate an export for a volume."""
pass
def remove_export(self, context, volume):
"""Remove an export for a volume."""
pass
class Huawei18000FCDriver(driver.FibreChannelDriver):
"""FC driver for Huawei OceanStor 18000 storage arrays.
Version history:
1.0.0 - Initial driver
1.1.0 - Provide Huawei OceanStor 18000 storage volume driver.
"""
VERSION = "1.1.0"
def __init__(self, *args, **kwargs):
super(Huawei18000FCDriver, self).__init__(*args, **kwargs)
def do_setup(self, context):
"""Instantiate common class and log in storage system."""
self.common = rest_common.RestCommon(configuration=self.configuration)
return self.common.login()
def check_for_setup_error(self):
"""Check configuration file."""
return self.common._check_conf_file()
def create_volume(self, volume):
"""Create a volume."""
lun_info = self.common.create_volume(volume)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot."""
lun_info = self.common.create_volume_from_snapshot(volume, snapshot)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def create_cloned_volume(self, volume, src_vref):
"""Create a clone of the specified volume."""
lun_info = self.common.create_cloned_volume(volume, src_vref)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def extend_volume(self, volume, new_size):
"""Extend a volume."""
return self.common.extend_volume(volume, new_size)
def delete_volume(self, volume):
"""Delete a volume."""
return self.common.delete_volume(volume)
def create_snapshot(self, snapshot):
"""Create a snapshot."""
lun_info = self.common.create_snapshot(snapshot)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def delete_snapshot(self, snapshot):
"""Delete a snapshot."""
return self.common.delete_snapshot(snapshot)
def get_volume_stats(self, refresh=False):
"""Get volume stats."""
data = self.common.update_volume_stats()
backend_name = self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = backend_name or self.__class__.__name__
data['storage_protocol'] = 'FC'
data['driver_version'] = self.VERSION
return data
@fczm_utils.AddFCZone
def initialize_connection(self, volume, connector):
"""Map a volume to a host."""
return self.common.initialize_connection_fc(volume, connector)
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
"""Terminate the map."""
return self.common.terminate_connection_fc(volume, connector)
def create_export(self, context, volume):
"""Export the volume."""
pass
def ensure_export(self, context, volume):
"""Synchronously recreate an export for a volume."""
pass
def remove_export(self, context, volume):
"""Remove an export for a volume."""
pass

View File

@ -0,0 +1,633 @@
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
# 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.
import uuid
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import units
from cinder import exception
from cinder.i18n import _, _LI, _LW
from cinder import utils
from cinder.volume import driver
from cinder.volume.drivers.huawei import constants
from cinder.volume.drivers.huawei import huawei_utils
from cinder.volume.drivers.huawei import rest_client
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
huawei_opt = [
cfg.StrOpt('cinder_huawei_conf_file',
default='/etc/cinder/cinder_huawei_conf.xml',
help='The configuration file for the Cinder Huawei '
'driver.')]
CONF = cfg.CONF
CONF.register_opts(huawei_opt)
class HuaweiBaseDriver(driver.VolumeDriver):
def __init__(self, *args, **kwargs):
super(HuaweiBaseDriver, self).__init__(*args, **kwargs)
self.configuration = kwargs.get('configuration', None)
if not self.configuration:
msg = _('_instantiate_driver: configuration not found.')
raise exception.InvalidInput(reason=msg)
self.configuration.append_config_values(huawei_opt)
self.xml_file_path = self.configuration.cinder_huawei_conf_file
def do_setup(self, context):
"""Instantiate common class and login storage system."""
self.restclient = rest_client.RestClient(self.configuration)
return self.restclient.login()
def check_for_setup_error(self):
"""Check configuration file."""
return huawei_utils.check_conf_file(self.xml_file_path)
def get_volume_stats(self, refresh=False):
"""Get volume status."""
return self.restclient.update_volume_stats()
@utils.synchronized('huawei', external=True)
def create_volume(self, volume):
"""Create a volume."""
pool_info = self.restclient.find_pool_info()
volume_name = huawei_utils.encode_name(volume['id'])
volume_description = volume['name']
volume_size = huawei_utils.get_volume_size(volume)
LOG.info(_LI(
'Create volume: %(volume)s, size: %(size)s.'),
{'volume': volume_name,
'size': volume_size})
params = huawei_utils.get_lun_conf_params(self.xml_file_path)
params['pool_id'] = pool_info['ID']
params['volume_size'] = volume_size
params['volume_description'] = volume_description
# Prepare LUN parameters.
lun_param = huawei_utils.init_lun_parameters(volume_name, params)
# Create LUN on the array.
lun_info = self.restclient.create_volume(lun_param)
lun_id = lun_info['ID']
return {'provider_location': lun_info['ID'],
'ID': lun_id,
'lun_info': lun_info}
@utils.synchronized('huawei', external=True)
def delete_volume(self, volume):
"""Delete a volume.
Three steps:
Firstly, remove associate from lungroup.
Secondly, remove associate from QoS policy.
Thirdly, remove the lun.
"""
name = huawei_utils.encode_name(volume['id'])
lun_id = volume.get('provider_location', None)
LOG.info(_LI('Delete volume: %(name)s, array lun id: %(lun_id)s.'),
{'name': name, 'lun_id': lun_id},)
if lun_id:
if self.restclient.check_lun_exist(lun_id):
self.restclient.delete_lun(lun_id)
else:
LOG.warning(_LW("Can't find %s on the array."), lun_id)
return False
return True
def create_volume_from_snapshot(self, volume, snapshot):
"""Create a volume from a snapshot.
We use LUNcopy to copy a new volume from snapshot.
The time needed increases as volume size does.
"""
snapshotname = huawei_utils.encode_name(snapshot['id'])
snapshot_id = snapshot.get('provider_location', None)
if snapshot_id is None:
snapshot_id = self.restclient.get_snapshotid_by_name(snapshotname)
if snapshot_id is None:
err_msg = (_(
'create_volume_from_snapshot: Snapshot %(name)s '
'does not exist.')
% {'name': snapshotname})
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
lun_info = self.create_volume(volume)
tgt_lun_id = lun_info['ID']
luncopy_name = huawei_utils.encode_name(volume['id'])
LOG.info(_LI(
'create_volume_from_snapshot: src_lun_id: %(src_lun_id)s, '
'tgt_lun_id: %(tgt_lun_id)s, copy_name: %(copy_name)s.'),
{'src_lun_id': snapshot_id,
'tgt_lun_id': tgt_lun_id,
'copy_name': luncopy_name})
event_type = 'LUNReadyWaitInterval'
wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
event_type)
def _volume_ready():
result = self.restclient.get_lun_info(tgt_lun_id)
if result['HEALTHSTATUS'] == "1":
if result['RUNNINGSTATUS'] == "27":
return True
return False
huawei_utils.wait_for_condition(self.xml_file_path,
_volume_ready,
wait_interval,
wait_interval * 10)
self._copy_volume(volume, luncopy_name,
snapshot_id, tgt_lun_id)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
def create_cloned_volume(self, volume, src_vref):
"""Clone a new volume from an existing volume."""
# Form the snapshot structure.
snapshot = {'id': uuid.uuid4().__str__(), 'volume_id': src_vref['id']}
# Create snapshot.
self.create_snapshot(snapshot)
try:
# Create volume from snapshot.
lun_info = self.create_volume_from_snapshot(volume, snapshot)
finally:
try:
# Delete snapshot.
self.delete_snapshot(snapshot)
except exception.VolumeBackendAPIException:
LOG.warning(_LW(
'Failure deleting the snapshot %(snapshot_id)s '
'of volume %(volume_id)s.'),
{'snapshot_id': snapshot['id'],
'volume_id': src_vref['id']},)
return {'provider_location': lun_info['ID'],
'lun_info': lun_info}
@utils.synchronized('huawei', external=True)
def extend_volume(self, volume, new_size):
"""Extend a volume."""
volume_size = huawei_utils.get_volume_size(volume)
new_volume_size = int(new_size) * units.Gi / 512
volume_name = huawei_utils.encode_name(volume['id'])
LOG.info(_LI(
'Extend volume: %(volumename)s, oldsize:'
' %(oldsize)s newsize: %(newsize)s.'),
{'volumename': volume_name,
'oldsize': volume_size,
'newsize': new_volume_size},)
lun_id = self.restclient.get_volume_by_name(volume_name)
if lun_id is None:
msg = (_(
"Can't find lun info on the array, lun name is: %(name)s.")
% {'name': volume_name})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
luninfo = self.restclient.extend_volume(lun_id, new_volume_size)
return {'provider_location': luninfo['ID'],
'lun_info': luninfo}
@utils.synchronized('huawei', external=True)
def create_snapshot(self, snapshot):
snapshot_info = self.restclient.create_snapshot(snapshot)
snapshot_id = snapshot_info['ID']
self.restclient.active_snapshot(snapshot_id)
return {'provider_location': snapshot_info['ID'],
'lun_info': snapshot_info}
@utils.synchronized('huawei', external=True)
def delete_snapshot(self, snapshot):
snapshotname = huawei_utils.encode_name(snapshot['id'])
volume_name = huawei_utils.encode_name(snapshot['volume_id'])
LOG.info(_LI(
'stop_snapshot: snapshot name: %(snapshot)s, '
'volume name: %(volume)s.'),
{'snapshot': snapshotname,
'volume': volume_name},)
snapshot_id = snapshot.get('provider_location', None)
if snapshot_id is None:
snapshot_id = self.restclient.get_snapshotid_by_name(snapshotname)
if snapshot_id is not None:
if self.restclient.check_snapshot_exist(snapshot_id):
self.restclient.stop_snapshot(snapshot_id)
self.restclient.delete_snapshot(snapshot_id)
else:
LOG.warning(_LW("Can't find snapshot on the array."))
else:
LOG.warning(_LW("Can't find snapshot on the array."))
return False
return True
@utils.synchronized('huawei', external=True)
def initialize_connection_fc(self, volume, connector):
wwns = connector['wwpns']
volume_name = huawei_utils.encode_name(volume['id'])
LOG.info(_LI(
'initialize_connection_fc, initiator: %(wwpns)s,'
' volume name: %(volume)s.'),
{'wwpns': wwns,
'volume': volume_name},)
host_name_before_hash = None
host_name = connector['host']
if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENTH):
host_name_before_hash = host_name
host_name = str(hash(host_name))
# Create hostgroup if not exist.
host_id = self.restclient.add_host_with_check(host_name,
host_name_before_hash)
# Add host into hostgroup.
hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
free_wwns = self.restclient.get_connected_free_wwns()
LOG.info(_LI("initialize_connection_fc, the array has free wwns: %s."),
free_wwns)
for wwn in wwns:
if wwn in free_wwns:
self.restclient.add_fc_port_to_host(host_id, wwn)
lun_id = self.restclient.mapping_hostgroup_and_lungroup(volume_name,
hostgroup_id,
host_id)
host_lun_id = self.restclient.find_host_lun_id(host_id, lun_id)
tgt_port_wwns = []
for wwn in wwns:
tgtwwpns = self.restclient.get_fc_target_wwpns(wwn)
if tgtwwpns:
tgt_port_wwns.append(tgtwwpns)
init_targ_map = {}
for initiator in wwns:
init_targ_map[initiator] = tgt_port_wwns
# Return FC properties.
info = {'driver_volume_type': 'fibre_channel',
'data': {'target_lun': int(host_lun_id),
'target_discovered': True,
'target_wwn': tgt_port_wwns,
'volume_id': volume['id'],
'initiator_target_map': init_targ_map}, }
LOG.info(_LI("initialize_connection_fc, return data is: %s."),
info)
return info
@utils.synchronized('huawei', external=True)
def initialize_connection_iscsi(self, volume, connector):
"""Map a volume to a host and return target iSCSI information."""
LOG.info(_LI('Enter initialize_connection_iscsi.'))
initiator_name = connector['initiator']
volume_name = huawei_utils.encode_name(volume['id'])
LOG.info(_LI(
'initiator name: %(initiator_name)s, '
'volume name: %(volume)s.'),
{'initiator_name': initiator_name,
'volume': volume_name})
(iscsi_iqn,
target_ip,
portgroup_id) = self.restclient.get_iscsi_params(self.xml_file_path,
connector)
LOG.info(_LI('initialize_connection_iscsi, iscsi_iqn: %(iscsi_iqn)s, '
'target_ip: %(target_ip)s, '
'TargetPortGroup: %(portgroup_id)s.'),
{'iscsi_iqn': iscsi_iqn,
'target_ip': target_ip,
'portgroup_id': portgroup_id},)
# Create hostgroup if not exist.
host_name = connector['host']
host_name_before_hash = None
if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENTH):
host_name_before_hash = host_name
host_name = str(hash(host_name))
host_id = self.restclient.add_host_with_check(host_name,
host_name_before_hash)
# Add initiator to the host.
self.restclient.ensure_initiator_added(initiator_name, host_id)
hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
# Mapping lungroup and hostgroup to view.
lun_id = self.restclient.mapping_hostgroup_and_lungroup(volume_name,
hostgroup_id,
host_id,
portgroup_id)
hostlun_id = self.restclient.find_host_lun_id(host_id, lun_id)
LOG.info(_LI("initialize_connection_iscsi, host lun id is: %s."),
hostlun_id)
# Return iSCSI properties.
properties = {}
properties['target_discovered'] = False
properties['target_portal'] = ('%s:%s' % (target_ip, '3260'))
properties['target_iqn'] = iscsi_iqn
properties['target_lun'] = int(hostlun_id)
properties['volume_id'] = volume['id']
LOG.info(_LI("initialize_connection_iscsi success. Return data: %s."),
properties)
return {'driver_volume_type': 'iscsi', 'data': properties}
@utils.synchronized('huawei', external=True)
def terminate_connection_iscsi(self, volume, connector):
"""Delete map between a volume and a host."""
initiator_name = connector['initiator']
volume_name = huawei_utils.encode_name(volume['id'])
lun_id = volume.get('provider_location', None)
host_name = connector['host']
LOG.info(_LI(
'terminate_connection_iscsi: volume name: %(volume)s, '
'initiator name: %(ini)s, '
'lun_id: %(lunid)s.'),
{'volume': volume_name,
'ini': initiator_name,
'lunid': lun_id},)
iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
portgroup = None
portgroup_id = None
left_lunnum = -1
for ini in iscsi_conf['Initiator']:
if ini['Name'] == initiator_name:
for key in ini:
if key == 'TargetPortGroup':
portgroup = ini['TargetPortGroup']
break
# Remove lun from lungroup.
if lun_id:
if self.restclient.check_lun_exist(lun_id):
# Get lungroup id by lun id.
lungroup_id = self.restclient.get_lungroupid_by_lunid(lun_id)
if lungroup_id:
self.restclient.remove_lun_from_lungroup(lungroup_id,
lun_id)
else:
LOG.warning(_LW("Can't find lun on the array."))
# Remove portgroup from mapping view if no lun left in lungroup.
if portgroup:
portgroup_id = self.restclient.find_tgt_port_group(portgroup)
host_id = self.restclient.find_host(host_name)
if host_id:
mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
view_id = self.restclient.find_mapping_view(mapping_view_name)
if view_id:
lungroup_id = self.restclient.find_lungroup_from_map(view_id)
if lungroup_id:
left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
if portgroup_id and view_id and (int(left_lunnum) <= 0):
if self.restclient.is_portgroup_associated_to_view(view_id,
portgroup_id):
self.restclient.delete_portgroup_mapping_view(view_id,
portgroup_id)
if view_id and (int(left_lunnum) <= 0):
if self.restclient.lungroup_associated(view_id, lungroup_id):
self.restclient.delete_lungroup_mapping_view(view_id,
lungroup_id)
self.restclient.delete_lungroup(lungroup_id)
if self.restclient.is_initiator_associated_to_host(initiator_name):
self.restclient.remove_iscsi_from_host(initiator_name)
hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
hostgroup_id = self.restclient.find_hostgroup(hostgroup_name)
if hostgroup_id:
if self.restclient.hostgroup_associated(view_id, hostgroup_id):
self.restclient.delete_hostgoup_mapping_view(view_id,
hostgroup_id)
self.restclient.remove_host_from_hostgroup(hostgroup_id,
host_id)
self.restclient.delete_hostgroup(hostgroup_id)
self.restclient.remove_host(host_id)
self.restclient.delete_mapping_view(view_id)
def terminate_connection_fc(self, volume, connector):
"""Delete map between a volume and a host."""
wwns = connector['wwpns']
volume_name = huawei_utils.encode_name(volume['id'])
lun_id = volume.get('provider_location', None)
host_name = connector['host']
left_lunnum = -1
LOG.info(_LI('terminate_connection_fc: volume name: %(volume)s, '
'wwpns: %(wwns)s, '
'lun_id: %(lunid)s.'),
{'volume': volume_name,
'wwns': wwns,
'lunid': lun_id},)
if lun_id:
if self.restclient.check_lun_exist(lun_id):
# Get lungroup id by lun id.
lungroup_id = self.restclient.get_lungroupid_by_lunid(lun_id)
if not lungroup_id:
LOG.info(_LI("Can't find lun in lungroup."))
else:
self.restclient.remove_lun_from_lungroup(lungroup_id,
lun_id)
else:
LOG.warning(_LW("Can't find lun on the array."))
tgt_port_wwns = []
for wwn in wwns:
tgtwwpns = self.restclient.get_fc_target_wwpns(wwn)
if tgtwwpns:
tgt_port_wwns.append(tgtwwpns)
init_targ_map = {}
for initiator in wwns:
init_targ_map[initiator] = tgt_port_wwns
host_id = self.restclient.find_host(host_name)
if host_id:
mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
view_id = self.restclient.find_mapping_view(mapping_view_name)
if view_id:
lungroup_id = self.restclient.find_lungroup_from_map(view_id)
if lungroup_id:
left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
if int(left_lunnum) > 0:
info = {'driver_volume_type': 'fibre_channel',
'data': {}}
else:
info = {'driver_volume_type': 'fibre_channel',
'data': {'target_wwn': tgt_port_wwns,
'initiator_target_map': init_targ_map}, }
return info
def migrate_volume(self, context, volume, host):
return (False, None)
def create_export(self, context, volume):
"""Export a volume."""
pass
def ensure_export(self, context, volume):
"""Synchronously recreate an export for a volume."""
pass
def remove_export(self, context, volume):
"""Remove an export for a volume."""
pass
def _copy_volume(self, volume, copy_name, src_lun, tgt_lun):
luncopy_id = self.restclient.create_luncopy(copy_name,
src_lun, tgt_lun)
event_type = 'LUNcopyWaitInterval'
wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
event_type)
try:
self.restclient.start_luncopy(luncopy_id)
def _luncopy_complete():
luncopy_info = self.restclient.get_luncopy_info(luncopy_id)
if luncopy_info['status'] == '40':
# luncopy_info['status'] means for the running status of
# the luncopy. If luncopy_info['status'] is equal to '40',
# this luncopy is completely ready.
return True
elif luncopy_info['state'] != '1':
# luncopy_info['state'] means for the healthy status of the
# luncopy. If luncopy_info['state'] is not equal to '1',
# this means that an error occurred during the LUNcopy
# operation and we should abort it.
err_msg = (_(
'An error occurred during the LUNcopy operation. '
'LUNcopy name: %(luncopyname)s. '
'LUNcopy status: %(luncopystatus)s. '
'LUNcopy state: %(luncopystate)s.')
% {'luncopyname': luncopy_id,
'luncopystatus': luncopy_info['status'],
'luncopystate': luncopy_info['state']},)
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
huawei_utils.wait_for_condition(self.xml_file_path,
_luncopy_complete,
wait_interval)
except Exception:
with excutils.save_and_reraise_exception():
self.restclient.delete_luncopy(luncopy_id)
self.delete_volume(volume)
self.restclient.delete_luncopy(luncopy_id)
class Huawei18000ISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver):
"""ISCSI driver for Huawei OceanStor 18000 storage arrays.
Version history:
1.0.0 - Initial driver
1.1.0 - Provide Huawei OceanStor 18000 storage volume driver.
"""
VERSION = "1.1.1"
def __init__(self, *args, **kwargs):
super(Huawei18000ISCSIDriver, self).__init__(*args, **kwargs)
def get_volume_stats(self, refresh=False):
"""Get volume status."""
data = HuaweiBaseDriver.get_volume_stats(self, refresh=False)
backend_name = self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = backend_name or self.__class__.__name__
data['storage_protocol'] = 'iSCSI'
data['driver_version'] = self.VERSION
data['vendor_name'] = 'Huawei'
return data
def initialize_connection(self, volume, connector):
return HuaweiBaseDriver.initialize_connection_iscsi(self,
volume,
connector)
def terminate_connection(self, volume, connector, **kwargs):
return HuaweiBaseDriver.terminate_connection_iscsi(self,
volume,
connector)
class Huawei18000FCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
"""FC driver for Huawei OceanStor 18000 storage arrays.
Version history:
1.0.0 - Initial driver
1.1.0 - Provide Huawei OceanStor 18000 storage volume driver.
"""
VERSION = "1.1.1"
def __init__(self, *args, **kwargs):
super(Huawei18000FCDriver, self).__init__(*args, **kwargs)
def get_volume_stats(self, refresh=False):
"""Get volume status."""
data = HuaweiBaseDriver.get_volume_stats(self, refresh=False)
backend_name = self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = backend_name or self.__class__.__name__
data['storage_protocol'] = 'FC'
data['driver_version'] = self.VERSION
data['verdor_name'] = 'Huawei'
return data
@fczm_utils.AddFCZone
def initialize_connection(self, volume, connector):
return HuaweiBaseDriver.initialize_connection_fc(self,
volume,
connector)
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
return HuaweiBaseDriver.terminate_connection_fc(self,
volume,
connector)

View File

@ -1,5 +1,4 @@
# Copyright (c) 2013 Huawei Technologies Co., Ltd.
# Copyright (c) 2012 OpenStack LLC.
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -14,32 +13,133 @@
# License for the specific language governing permissions and limitations
# under the License.
import base64
import six
import time
import uuid
from xml.etree import ElementTree as ET
from oslo_log import log as logging
from oslo_service import loopingcall
from oslo_utils import units
from cinder.i18n import _LE
from cinder import context
from cinder import exception
from cinder import utils
from cinder.i18n import _, _LE, _LI
from cinder.volume.drivers.huawei import constants
from cinder.volume import qos_specs
from cinder.volume import volume_types
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'}
opts_capability = {
'smarttier': False,
'smartcache': False,
'smartpartition': False,
'thin_provisioning': False,
'thick_provisioning': False,
}
def parse_xml_file(filepath):
opts_value = {
'policy': None,
'partitionname': None,
'cachename': None,
}
opts_associate = {
'smarttier': 'policy',
'smartcache': 'cachename',
'smartpartition': 'partitionname',
}
def get_volume_params(volume):
opts = {}
ctxt = context.get_admin_context()
type_id = volume['volume_type_id']
if type_id is not None:
volume_type = volume_types.get_volume_type(ctxt, type_id)
specs = dict(volume_type).get('extra_specs')
opts = _get_extra_spec_value(specs)
else:
opts.update(opts_capability)
opts.update(opts_value)
return opts
def _get_extra_spec_value(specs):
"""Return the parameters for creating the volume."""
opts = {}
opts.update(opts_capability)
opts.update(opts_value)
opts = _get_opts_from_specs(opts_capability, opts_value, specs)
LOG.debug('get_volume_params opts %(opts)s.', {'opts': opts})
return opts
def _get_opts_from_specs(opts_capability, opts_value, specs):
opts = {}
opts.update(opts_capability)
opts.update(opts_value)
for key, value in specs.iteritems():
# Get the scope, if is using scope format.
scope = None
key_split = key.split(':')
if len(key_split) > 2 and key_split[0] != "capabilities":
continue
if len(key_split) == 1:
key = key_split[0]
else:
scope = key_split[0]
key = key_split[1]
if scope:
scope = scope.lower()
if key:
key = key.lower()
if (scope in opts_capability) and (key in opts_value):
if (scope in opts_associate) and (opts_associate[scope] == key):
opts[key] = value
return opts
def _get_smartx_specs_params(lunsetinfo, smartx_opts):
"""Get parameters from config file for creating lun."""
# Default lun set information.
if 'LUNType' in smartx_opts:
lunsetinfo['LUNType'] = smartx_opts['LUNType']
lunsetinfo['policy'] = smartx_opts['policy']
return lunsetinfo
def get_lun_params(xml_file_path, smartx_opts):
lunsetinfo = {}
lunsetinfo = get_lun_conf_params(xml_file_path)
lunsetinfo = _get_smartx_specs_params(lunsetinfo, smartx_opts)
return lunsetinfo
def parse_xml_file(xml_file_path):
"""Get root of xml file."""
try:
tree = ET.parse(filepath)
tree = ET.parse(xml_file_path)
root = tree.getroot()
return root
except IOError as err:
LOG.error(_LE('parse_xml_file: %s'), err)
LOG.error(_LE('parse_xml_file: %s.'), err)
raise
@ -64,49 +164,7 @@ def get_xml_item(xml_root, item):
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)
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):
def get_conf_host_os_type(host_ip, conf):
"""Get host OS type from xml config file.
:param host_ip: The IP of Nova host
@ -114,7 +172,8 @@ def get_conf_host_os_type(host_ip, config):
:return: host OS type
"""
os_conf = {}
root = parse_xml_file(config)
xml_file_path = conf.cinder_huawei_conf_file
root = parse_xml_file(xml_file_path)
hosts_list = get_xml_item(root, 'Host')
for host in hosts_list:
os = host['attrib']['OSType'].strip()
@ -123,11 +182,302 @@ def get_conf_host_os_type(host_ip, config):
host_os = None
for k, v in os_conf.items():
if host_ip in v:
host_os = os_type.get(k, None)
host_os = constants.OS_TYPE.get(k, None)
if not host_os:
host_os = os_type['Linux'] # default os type
host_os = constants.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
def get_qos_by_volume_type(volume_type):
qos = {}
qos_specs_id = volume_type.get('qos_specs_id')
# We prefer the qos_specs association
# and override any existing extra-specs settings
# if present.
if qos_specs_id is not None:
kvs = qos_specs.get_qos_specs(context.get_admin_context(),
qos_specs_id)['specs']
else:
return qos
LOG.info(_LI('The QoS sepcs is: %s.'), kvs)
for key, value in kvs.iteritems():
if key in constants.HUAWEI_VALID_KEYS:
if (key.upper() != 'IOTYPE') and (int(value) <= 0):
err_msg = (_('Qos config is wrong. %(key)s'
' must be set greater than 0.')
% {'key': key})
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
elif (key.upper() == 'IOTYPE') and (value not in ['0', '1', '2']):
raise exception.InvalidInput(
reason=(_('Illegal value specified for IOTYPE: '
'set to either 0, 1, or 2.')))
else:
qos[key.upper()] = value
return qos
def get_volume_qos(volume):
qos = {}
ctxt = context.get_admin_context()
type_id = volume['volume_type_id']
if type_id is not None:
volume_type = volume_types.get_volume_type(ctxt, type_id)
qos = get_qos_by_volume_type(volume_type)
return qos
def _get_volume_type(type_id):
ctxt = context.get_admin_context()
return volume_types.get_volume_type(ctxt, type_id)
def get_lun_conf_params(xml_file_path):
"""Get parameters from config file for creating lun."""
lunsetinfo = {
'LUNType': 'Thick',
'StripUnitSize': '64',
'WriteType': '1',
'MirrorSwitch': '1',
'PrefetchType': '3',
'PrefetchValue': '0',
'PrefetchTimes': '0',
'policy': '0',
'readcachepolicy': '2',
'writecachepolicy': '5',
}
# Default lun set information.
root = parse_xml_file(xml_file_path)
luntype = root.findtext('LUN/LUNType')
if luntype:
if luntype.strip() in ['Thick', 'Thin']:
lunsetinfo['LUNType'] = luntype.strip()
if luntype.strip() == 'Thick':
lunsetinfo['LUNType'] = 0
if luntype.strip() == 'Thin':
lunsetinfo['LUNType'] = 1
elif luntype is not '' and luntype is not None:
err_msg = (_(
'Config file is wrong. LUNType must be "Thin"'
' or "Thick". LUNType: %(fetchtype)s.')
% {'fetchtype': luntype})
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
stripunitsize = root.findtext('LUN/StripUnitSize')
if stripunitsize is not None:
lunsetinfo['StripUnitSize'] = stripunitsize.strip()
writetype = root.findtext('LUN/WriteType')
if writetype is not None:
lunsetinfo['WriteType'] = writetype.strip()
mirrorswitch = root.findtext('LUN/MirrorSwitch')
if mirrorswitch is not None:
lunsetinfo['MirrorSwitch'] = mirrorswitch.strip()
prefetch = root.find('LUN/Prefetch')
fetchtype = prefetch.attrib['Type']
if prefetch is not None and prefetch.attrib['Type']:
if fetchtype in ['0', '1', '2', '3']:
lunsetinfo['PrefetchType'] = fetchtype.strip()
typevalue = prefetch.attrib['Value'].strip()
if lunsetinfo['PrefetchType'] == '1':
double_value = int(typevalue) * 2
typevalue_double = six.text_type(double_value)
lunsetinfo['PrefetchValue'] = typevalue_double
elif lunsetinfo['PrefetchType'] == '2':
lunsetinfo['PrefetchValue'] = typevalue
else:
err_msg = (_(
'PrefetchType config is wrong. PrefetchType'
' must be in 0,1,2,3. PrefetchType is: %(fetchtype)s.')
% {'fetchtype': fetchtype})
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
else:
LOG.info(_LI(
'Use default PrefetchType. '
'PrefetchType: Intelligent.'))
return lunsetinfo
def encode_name(name):
uuid_str = name.replace("-", "")
vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str)
vol_encoded = base64.urlsafe_b64encode(vol_uuid.bytes)
vol_encoded = vol_encoded.decode("utf-8") # Make it compatible to py3.
newuuid = vol_encoded.replace("=", "")
return newuuid
def init_lun_parameters(name, parameters):
"""Initialize basic LUN parameters."""
lunparam = {"TYPE": "11",
"NAME": name,
"PARENTTYPE": "216",
"PARENTID": parameters['pool_id'],
"DESCRIPTION": parameters['volume_description'],
"ALLOCTYPE": parameters['LUNType'],
"CAPACITY": parameters['volume_size'],
"WRITEPOLICY": parameters['WriteType'],
"MIRRORPOLICY": parameters['MirrorSwitch'],
"PREFETCHPOLICY": parameters['PrefetchType'],
"PREFETCHVALUE": parameters['PrefetchValue'],
"DATATRANSFERPOLICY": parameters['policy'],
"READCACHEPOLICY": parameters['readcachepolicy'],
"WRITECACHEPOLICY": parameters['writecachepolicy'],
}
return lunparam
def volume_in_use(volume):
"""Check if the given volume is in use."""
return (volume['volume_attachment'] and
len(volume['volume_attachment']) > 0)
def get_wait_interval(xml_file_path, event_type):
"""Get wait interval from huawei conf file."""
root = parse_xml_file(xml_file_path)
wait_interval = root.findtext('LUN/%s' % event_type)
if wait_interval is None:
wait_interval = constants.DEFAULT_WAIT_INTERVAL
LOG.info(_LI(
"Wait interval for %(event_type)s is not configured in huawei "
"conf file. Use default: %(default_wait_interval)d."),
{"event_type": event_type,
"default_wait_interval": wait_interval})
return int(wait_interval)
def _get_default_timeout(xml_file_path):
"""Get timeout from huawei conf file."""
root = parse_xml_file(xml_file_path)
timeout = root.findtext('LUN/Timeout')
if timeout is None:
timeout = constants.DEFAULT_WAIT_TIMEOUT
LOG.info(_LI(
"Timeout is not configured in huawei conf file. "
"Use default: %(default_timeout)d."),
{"default_timeout": timeout})
return timeout
def wait_for_condition(xml_file_path, func, interval, timeout=None):
start_time = time.time()
if timeout is None:
timeout = _get_default_timeout(xml_file_path)
def _inner():
try:
res = func()
except Exception as ex:
raise exception.VolumeBackendAPIException(ex)
if res:
raise loopingcall.LoopingCallDone()
if int(time.time()) - start_time > timeout:
msg = (_('wait_for_condition: %s timed out.')
% func.__name__)
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
timer = loopingcall.FixedIntervalLoopingCall(_inner)
timer.start(interval=interval).wait()
def get_login_info(xml_file_path):
"""Get login IP, user name and password from config file."""
logininfo = {}
root = parse_xml_file(xml_file_path)
logininfo['RestURL'] = root.findtext('Storage/RestURL').strip()
for key in ['UserName', 'UserPassword']:
node = root.find('Storage/%s' % key)
node_text = node.text
logininfo[key] = node_text
return logininfo
def _change_file_mode(filepath):
utils.execute('chmod', '640', filepath, run_as_root=True)
def get_iscsi_conf(xml_file_path):
"""Get iSCSI info from config file."""
iscsiinfo = {}
root = parse_xml_file(xml_file_path)
TargetIP = root.findtext('iSCSI/DefaultTargetIP').strip()
iscsiinfo['DefaultTargetIP'] = TargetIP
initiator_list = []
for dic in root.findall('iSCSI/Initiator'):
# Strip values of dict.
tmp_dic = {}
for k in dic.items():
tmp_dic[k[0]] = k[1].strip()
initiator_list.append(tmp_dic)
iscsiinfo['Initiator'] = initiator_list
return iscsiinfo
def check_qos_high_priority(qos):
"""Check QoS priority."""
for key, value in qos.iteritems():
if (key.find('MIN') == 0) or (key.find('LATENCY') == 0):
return True
return False
def check_conf_file(xml_file_path):
"""Check the config file, make sure the essential items are set."""
root = parse_xml_file(xml_file_path)
resturl = root.findtext('Storage/RestURL')
username = root.findtext('Storage/UserName')
pwd = root.findtext('Storage/UserPassword')
pool_node = root.findall('Storage/StoragePool')
if (not resturl) or (not username) or (not pwd):
err_msg = (_(
'check_conf_file: Config file invalid. RestURL,'
' UserName and UserPassword 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)
def get_volume_size(volume):
"""Calculate the volume size.
We should divide the given volume size by 512 for the 18000 system
calculates volume size with sectors, which is 512 bytes.
"""
volume_size = units.Gi / 512 # 1G
if int(volume['size']) != 0:
volume_size = int(volume['size']) * units.Gi / 512
return volume_size

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -109,10 +109,10 @@ CONF = cfg.CONF
CONF.register_opts(volume_manager_opts)
MAPPING = {
'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSISCSIDriver':
'cinder.volume.drivers.huawei.huawei_18000.Huawei18000ISCSIDriver',
'cinder.volume.drivers.huawei.huawei_hvs.HuaweiHVSFCDriver':
'cinder.volume.drivers.huawei.huawei_18000.Huawei18000FCDriver',
'cinder.volume.drivers.huawei.huawei_18000.Huawei18000ISCSIDriver':
'cinder.volume.drivers.huawei.huawei_driver.Huawei18000ISCSIDriver',
'cinder.volume.drivers.huawei.huawei_18000.Huawei18000FCDriver':
'cinder.volume.drivers.huawei.huawei_driver.Huawei18000FCDriver',
'cinder.volume.drivers.fujitsu_eternus_dx_fc.FJDXFCDriver':
'cinder.volume.drivers.fujitsu.eternus_dx_fc.FJDXFCDriver',
'cinder.volume.drivers.fujitsu_eternus_dx_iscsi.FJDXISCSIDriver':

View File

@ -66,7 +66,7 @@ commands =
cinder.tests.unit.test_hitachi_hbsd_snm2_iscsi \
cinder.tests.unit.test_hp_xp_fc \
cinder.tests.unit.test_hplefthand \
cinder.tests.unit.test_huawei_18000 \
cinder.tests.unit.test_huawei_drivers \
cinder.tests.unit.test_huawei_drivers_compatibility \
cinder.tests.unit.test_ibm_xiv_ds8k \
cinder.tests.unit.test_infortrend_cli \