Merge "Huawei: Refactor driver for the second time"
This commit is contained in:
commit
65dbadedde
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
|
||||
# Copyright (c) 2016 Huawei Technologies Co., Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
|
||||
# Copyright (c) 2016 Huawei Technologies Co., Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -23,14 +23,14 @@ LOG = logging.getLogger(__name__)
|
||||
class FCZoneHelper(object):
|
||||
"""FC zone helper for Huawei driver."""
|
||||
|
||||
def __init__(self, fcsan_lookup_service, restclient):
|
||||
def __init__(self, fcsan_lookup_service, client):
|
||||
self.fcsan_lookup_service = fcsan_lookup_service
|
||||
self.restclient = restclient
|
||||
self.client = client
|
||||
|
||||
def _get_fc_port_contr_map(self):
|
||||
port_list = []
|
||||
port_contr_map = {}
|
||||
data = self.restclient.get_fc_ports_on_array()
|
||||
data = self.client.get_fc_ports_on_array()
|
||||
for item in data:
|
||||
if item['RUNNINGSTATUS'] == constants.FC_PORT_CONNECTED:
|
||||
port_list.append(item['WWN'])
|
||||
|
254
cinder/volume/drivers/huawei/huawei_conf.py
Normal file
254
cinder/volume/drivers/huawei/huawei_conf.py
Normal file
@ -0,0 +1,254 @@
|
||||
# Copyright (c) 2016 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.
|
||||
|
||||
"""
|
||||
Set Huawei private configuration into Configuration object.
|
||||
|
||||
For conveniently get private configuration. We parse Huawei config file
|
||||
and set every property into Configuration object as an attribute.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import six
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder import utils
|
||||
from cinder.volume.drivers.huawei import constants
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HuaweiConf(object):
|
||||
def __init__(self, conf):
|
||||
self.conf = conf
|
||||
|
||||
def _encode_authentication(self):
|
||||
need_encode = False
|
||||
tree = ET.parse(self.conf.cinder_huawei_conf_file)
|
||||
xml_root = tree.getroot()
|
||||
name_node = xml_root.find('Storage/UserName')
|
||||
pwd_node = xml_root.find('Storage/UserPassword')
|
||||
if (name_node is not None
|
||||
and not name_node.text.startswith('!$$$')):
|
||||
name_node.text = '!$$$' + base64.b64encode(name_node.text)
|
||||
need_encode = True
|
||||
if (pwd_node is not None
|
||||
and not pwd_node.text.startswith('!$$$')):
|
||||
pwd_node.text = '!$$$' + base64.b64encode(pwd_node.text)
|
||||
need_encode = True
|
||||
|
||||
if need_encode:
|
||||
utils.execute('chmod',
|
||||
'600',
|
||||
self.conf.cinder_huawei_conf_file,
|
||||
run_as_root=True)
|
||||
tree.write(self.conf.cinder_huawei_conf_file, 'UTF-8')
|
||||
|
||||
def update_config_value(self):
|
||||
self._encode_authentication()
|
||||
|
||||
set_attr_funcs = (self._san_address,
|
||||
self._san_user,
|
||||
self._san_password,
|
||||
self._san_product,
|
||||
self._san_protocol,
|
||||
self._lun_type,
|
||||
self._lun_ready_wait_interval,
|
||||
self._lun_copy_wait_interval,
|
||||
self._lun_timeout,
|
||||
self._lun_write_type,
|
||||
self._lun_mirror_switch,
|
||||
self._lun_prefetch,
|
||||
self._lun_policy,
|
||||
self._lun_read_cache_policy,
|
||||
self._lun_write_cache_policy,
|
||||
self._storage_pools,
|
||||
self._iscsi_default_target_ip,
|
||||
self._iscsi_info,)
|
||||
|
||||
tree = ET.parse(self.conf.cinder_huawei_conf_file)
|
||||
xml_root = tree.getroot()
|
||||
for f in set_attr_funcs:
|
||||
f(xml_root)
|
||||
|
||||
def _san_address(self, xml_root):
|
||||
text = xml_root.findtext('Storage/RestURL')
|
||||
if not text:
|
||||
msg = _("RestURL is not configured.")
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
addrs = text.split(';')
|
||||
addrs = list(set([x.strip() for x in addrs if x.strip()]))
|
||||
setattr(self.conf, 'san_address', addrs)
|
||||
|
||||
def _san_user(self, xml_root):
|
||||
text = xml_root.findtext('Storage/UserName')
|
||||
if not text:
|
||||
msg = _("UserName is not configured.")
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
user = base64.b64decode(text[4:])
|
||||
setattr(self.conf, 'san_user', user)
|
||||
|
||||
def _san_password(self, xml_root):
|
||||
text = xml_root.findtext('Storage/UserPassword')
|
||||
if not text:
|
||||
msg = _("UserPassword is not configured.")
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
pwd = base64.b64decode(text[4:])
|
||||
setattr(self.conf, 'san_password', pwd)
|
||||
|
||||
def _san_product(self, xml_root):
|
||||
text = xml_root.findtext('Storage/Product')
|
||||
if not text:
|
||||
msg = _("SAN product is not configured.")
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
product = text.strip()
|
||||
setattr(self.conf, 'san_product', product)
|
||||
|
||||
def _san_protocol(self, xml_root):
|
||||
text = xml_root.findtext('Storage/Protocol')
|
||||
if not text:
|
||||
msg = _("SAN protocol is not configured.")
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
protocol = text.strip()
|
||||
setattr(self.conf, 'san_protocol', protocol)
|
||||
|
||||
def _lun_type(self, xml_root):
|
||||
lun_type = constants.THICK_LUNTYPE
|
||||
|
||||
text = xml_root.findtext('LUN/LUNType')
|
||||
if text:
|
||||
lun_type = text.strip()
|
||||
if lun_type == 'Thick':
|
||||
lun_type = constants.THICK_LUNTYPE
|
||||
elif lun_type == 'Thin':
|
||||
lun_type = constants.THIN_LUNTYPE
|
||||
else:
|
||||
msg = (_("Invalid lun type %s is configured.") % lun_type)
|
||||
LOG.exception(msg)
|
||||
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
setattr(self.conf, 'lun_type', lun_type)
|
||||
|
||||
def _lun_ready_wait_interval(self, xml_root):
|
||||
text = xml_root.findtext('LUN/LUNReadyWaitInterval')
|
||||
interval = text.strip() if text else constants.DEFAULT_WAIT_INTERVAL
|
||||
setattr(self.conf, 'lun_ready_wait_interval', int(interval))
|
||||
|
||||
def _lun_copy_wait_interval(self, xml_root):
|
||||
text = xml_root.findtext('LUN/LUNcopyWaitInterval')
|
||||
interval = text.strip() if text else constants.DEFAULT_WAIT_INTERVAL
|
||||
setattr(self.conf, 'lun_copy_wait_interval', int(interval))
|
||||
|
||||
def _lun_timeout(self, xml_root):
|
||||
text = xml_root.findtext('LUN/Timeout')
|
||||
interval = text.strip() if text else constants.DEFAULT_WAIT_TIMEOUT
|
||||
setattr(self.conf, 'lun_timeout', int(interval))
|
||||
|
||||
def _lun_write_type(self, xml_root):
|
||||
text = xml_root.findtext('LUN/WriteType')
|
||||
write_type = text.strip() if text else '1'
|
||||
setattr(self.conf, 'lun_write_type', write_type)
|
||||
|
||||
def _lun_mirror_switch(self, xml_root):
|
||||
text = xml_root.findtext('LUN/MirrorSwitch')
|
||||
mirror_switch = text.strip() if text else '1'
|
||||
setattr(self.conf, 'lun_mirror_switch', mirror_switch)
|
||||
|
||||
def _lun_prefetch(self, xml_root):
|
||||
prefetch_type = '3'
|
||||
prefetch_value = '0'
|
||||
|
||||
node = xml_root.find('LUN/Prefetch')
|
||||
if (node is not None
|
||||
and node.attrib['Type']
|
||||
and node.attrib['Value']):
|
||||
prefetch_type = node.attrib['Type'].strip()
|
||||
if prefetch_type not in ['0', '1', '2', '3']:
|
||||
msg = (_(
|
||||
"Invalid prefetch type '%s' is configured. "
|
||||
"PrefetchType must be in 0,1,2,3.") % prefetch_type)
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
prefetch_value = node.attrib['Value'].strip()
|
||||
factor = {'1': 2, '2': 2}
|
||||
factor = int(factor.get('prefetch_type', '1'))
|
||||
prefetch_value = int(prefetch_value) * factor
|
||||
prefetch_value = six.text_type(prefetch_value)
|
||||
|
||||
setattr(self.conf, 'lun_prefetch_type', prefetch_type)
|
||||
setattr(self.conf, 'lun_prefetch_value', prefetch_value)
|
||||
|
||||
def _lun_policy(self, xml_root):
|
||||
setattr(self.conf, 'lun_policy', '0')
|
||||
|
||||
def _lun_read_cache_policy(self, xml_root):
|
||||
setattr(self.conf, 'lun_read_cache_policy', '2')
|
||||
|
||||
def _lun_write_cache_policy(self, xml_root):
|
||||
setattr(self.conf, 'lun_write_cache_policy', '5')
|
||||
|
||||
def _storage_pools(self, xml_root):
|
||||
nodes = xml_root.findall('LUN/StoragePool')
|
||||
if not nodes:
|
||||
msg = _('Storage pool is not configured.')
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
texts = [x.text for x in nodes]
|
||||
merged_text = ';'.join(texts)
|
||||
pools = set(x.strip() for x in merged_text.split(';') if x.strip())
|
||||
if not pools:
|
||||
msg = _('Invalid storage pool is configured.')
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(msg)
|
||||
|
||||
setattr(self.conf, 'storage_pools', list(pools))
|
||||
|
||||
def _iscsi_default_target_ip(self, xml_root):
|
||||
text = xml_root.findtext('iSCSI/DefaultTargetIP')
|
||||
target_ip = text.split() if text else []
|
||||
setattr(self.conf, 'iscsi_default_target_ip', target_ip)
|
||||
|
||||
def _iscsi_info(self, xml_root):
|
||||
nodes = xml_root.findall('iSCSI/Initiator')
|
||||
if nodes is None:
|
||||
setattr(self.conf, 'iscsi_info', [])
|
||||
return
|
||||
|
||||
iscsi_info = []
|
||||
for node in nodes:
|
||||
props = {}
|
||||
for item in node.items():
|
||||
props[item[0].strip()] = item[1].strip()
|
||||
|
||||
iscsi_info.append(props)
|
||||
|
||||
setattr(self.conf, 'iscsi_info', iscsi_info)
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
|
||||
# Copyright (c) 2016 Huawei Technologies Co., Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -18,334 +18,19 @@ import json
|
||||
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 import context
|
||||
from cinder import exception
|
||||
from cinder import utils
|
||||
from cinder.i18n import _, _LE, _LI
|
||||
from cinder.i18n import _
|
||||
from cinder import objects
|
||||
from cinder.volume.drivers.huawei import constants
|
||||
from cinder.volume import qos_specs
|
||||
from cinder.volume import volume_types
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
opts_capability = {
|
||||
'smarttier': False,
|
||||
'smartcache': False,
|
||||
'smartpartition': False,
|
||||
'thin_provisioning_support': False,
|
||||
'thick_provisioning_support': False,
|
||||
'hypermetro': False,
|
||||
}
|
||||
|
||||
|
||||
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.items():
|
||||
|
||||
# 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 ((not scope or scope == 'capabilities')
|
||||
and key in opts_capability):
|
||||
|
||||
words = value.split()
|
||||
|
||||
if not (words and len(words) == 2 and words[0] == '<is>'):
|
||||
LOG.error(_LE("Extra specs must be specified as "
|
||||
"capabilities:%s='<is> True' or "
|
||||
"'<is> true'."), key)
|
||||
else:
|
||||
opts[key] = words[1].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 = 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(xml_file_path)
|
||||
root = tree.getroot()
|
||||
return root
|
||||
except IOError as err:
|
||||
LOG.error(_LE('parse_xml_file: %s.'), err)
|
||||
raise
|
||||
|
||||
|
||||
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 get_conf_host_os_type(host_ip, conf):
|
||||
"""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 = {}
|
||||
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()
|
||||
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 = constants.OS_TYPE.get(k, None)
|
||||
if not host_os:
|
||||
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.items():
|
||||
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': 0,
|
||||
'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
|
||||
elif luntype.strip() == 'Thin':
|
||||
lunsetinfo['LUNType'] = 1
|
||||
|
||||
else:
|
||||
err_msg = (_(
|
||||
"LUNType config is wrong. LUNType must be 'Thin'"
|
||||
" or 'Thick'. LUNType: %(fetchtype)s.")
|
||||
% {'fetchtype': luntype})
|
||||
LOG.error(err_msg)
|
||||
raise exception.VolumeBackendAPIException(data=err_msg)
|
||||
else:
|
||||
lunsetinfo['LUNType'] = 0
|
||||
|
||||
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')
|
||||
if prefetch is not None and prefetch.attrib['Type']:
|
||||
fetchtype = 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 find_luntype_in_xml(xml_file_path):
|
||||
root = parse_xml_file(xml_file_path)
|
||||
luntype = root.findtext('LUN/LUNType')
|
||||
if luntype:
|
||||
if luntype.strip() in ['Thick', 'Thin']:
|
||||
if luntype.strip() == 'Thick':
|
||||
luntype = constants.THICK_LUNTYPE
|
||||
elif luntype.strip() == 'Thin':
|
||||
luntype = constants.THIN_LUNTYPE
|
||||
else:
|
||||
err_msg = (_(
|
||||
"LUNType config is wrong. LUNType must be 'Thin'"
|
||||
" or 'Thick'. LUNType: %(fetchtype)s.")
|
||||
% {'fetchtype': luntype})
|
||||
LOG.error(err_msg)
|
||||
raise exception.VolumeBackendAPIException(data=err_msg)
|
||||
else:
|
||||
luntype = constants.THICK_LUNTYPE
|
||||
return luntype
|
||||
|
||||
|
||||
def encode_name(name):
|
||||
uuid_str = name.replace("-", "")
|
||||
vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str)
|
||||
@ -355,72 +40,21 @@ def encode_name(name):
|
||||
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 encode_host_name(name):
|
||||
if name and (len(name) > constants.MAX_HOSTNAME_LENGTH):
|
||||
name = six.text_type(hash(name))
|
||||
return name
|
||||
|
||||
|
||||
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):
|
||||
def wait_for_condition(func, interval, timeout):
|
||||
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(data=ex)
|
||||
|
||||
if res:
|
||||
raise loopingcall.LoopingCallDone()
|
||||
|
||||
@ -434,78 +68,6 @@ def wait_for_condition(xml_file_path, func, interval, timeout=None):
|
||||
timer.start(interval=interval).wait()
|
||||
|
||||
|
||||
def get_login_info(xml_file_path):
|
||||
"""Get login IP, user name and password from config file."""
|
||||
login_info = {}
|
||||
root = parse_xml_file(xml_file_path)
|
||||
|
||||
login_info['RestURL'] = root.findtext('Storage/RestURL').strip()
|
||||
|
||||
for key in ['UserName', 'UserPassword']:
|
||||
node = root.find('Storage/%s' % key)
|
||||
node_text = node.text
|
||||
login_info[key] = node_text
|
||||
|
||||
return login_info
|
||||
|
||||
|
||||
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)
|
||||
target_ip = root.findtext('iSCSI/DefaultTargetIP').strip()
|
||||
iscsiinfo['DefaultTargetIP'] = target_ip
|
||||
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.items():
|
||||
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('LUN/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.
|
||||
|
||||
@ -519,30 +81,6 @@ def get_volume_size(volume):
|
||||
return volume_size
|
||||
|
||||
|
||||
def get_protocol(xml_file_path):
|
||||
"""Get protocol from huawei conf file."""
|
||||
root = parse_xml_file(xml_file_path)
|
||||
protocol = root.findtext('Storage/Protocol')
|
||||
if not protocol:
|
||||
err_msg = (_('Get protocol from huawei conf file error.'))
|
||||
LOG.error(err_msg)
|
||||
raise exception.InvalidInput(reason=err_msg)
|
||||
|
||||
return protocol
|
||||
|
||||
|
||||
def get_pools(xml_file_path):
|
||||
"""Get pools from huawei conf file."""
|
||||
root = parse_xml_file(xml_file_path)
|
||||
pool_names = root.findtext('LUN/StoragePool')
|
||||
if not pool_names:
|
||||
msg = _('Invalid resource pool name. '
|
||||
'Please check the config file.')
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(msg)
|
||||
return pool_names
|
||||
|
||||
|
||||
def get_remote_device_info(valid_hypermetro_devices):
|
||||
remote_device_info = {}
|
||||
try:
|
||||
@ -562,8 +100,22 @@ def get_remote_device_info(valid_hypermetro_devices):
|
||||
|
||||
|
||||
def get_volume_metadata(volume):
|
||||
if type(volume) is objects.Volume:
|
||||
return volume.metadata
|
||||
|
||||
if 'volume_metadata' in volume:
|
||||
metadata = volume.get('volume_metadata')
|
||||
return {item['key']: item['value'] for item in metadata}
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def get_snapshot_metadata_value(snapshot):
|
||||
if type(snapshot) is objects.Snapshot:
|
||||
return snapshot.metadata
|
||||
|
||||
if 'snapshot_metadata' in snapshot:
|
||||
metadata = snapshot.get('snapshot_metadata')
|
||||
return {item['key']: item['value'] for item in metadata}
|
||||
|
||||
return {}
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
|
||||
# Copyright (c) 2016 Huawei Technologies Co., Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -14,67 +14,59 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import six
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LI, _LW
|
||||
from cinder.volume.drivers.huawei import constants
|
||||
from cinder.volume.drivers.huawei import huawei_utils
|
||||
from cinder.volume.drivers.huawei import rest_client
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HuaweiHyperMetro(object):
|
||||
|
||||
def __init__(self, client, rmt_client, configuration):
|
||||
def __init__(self, client, rmt_client, configuration, db):
|
||||
self.db = db
|
||||
self.client = client
|
||||
self.rmt_client = rmt_client
|
||||
self.configuration = configuration
|
||||
self.xml_file_path = self.configuration.cinder_huawei_conf_file
|
||||
|
||||
def create_hypermetro(self, local_lun_id, lun_param):
|
||||
def create_hypermetro(self, local_lun_id, lun_params):
|
||||
"""Create hypermetro."""
|
||||
metro_devices = self.configuration.hypermetro_devices
|
||||
device_info = huawei_utils.get_remote_device_info(metro_devices)
|
||||
self.rmt_client = rest_client.RestClient(self.configuration)
|
||||
self.rmt_client.login_with_ip(device_info)
|
||||
|
||||
try:
|
||||
# Get the remote pool info.
|
||||
config_pool = device_info['StoragePool']
|
||||
remote_pool = self.rmt_client.find_all_pools()
|
||||
pool = self.rmt_client.find_pool_info(config_pool,
|
||||
remote_pool)
|
||||
# Create remote lun
|
||||
lun_param['PARENTID'] = pool['ID']
|
||||
remotelun_info = self.rmt_client.create_volume(lun_param)
|
||||
config_pool = self.configuration.metro_storage_pools
|
||||
remote_pool = self.rmt_client.get_all_pools()
|
||||
pool = self.rmt_client.get_pool_info(config_pool, remote_pool)
|
||||
# Create remote lun.
|
||||
lun_params['pool_id'] = pool['ID']
|
||||
remotelun_info = self.rmt_client.create_lun(lun_params)
|
||||
remote_lun_id = remotelun_info['ID']
|
||||
|
||||
# Get hypermetro domain
|
||||
# Get hypermetro domain.
|
||||
try:
|
||||
domain_name = device_info['domain_name']
|
||||
domain_name = self.configuration.metro_domain_name
|
||||
domain_id = self.rmt_client.get_hyper_domain_id(domain_name)
|
||||
self._wait_volume_ready(remote_lun_id)
|
||||
hypermetro = self._create_hypermetro_pair(domain_id,
|
||||
local_lun_id,
|
||||
remote_lun_id)
|
||||
|
||||
return hypermetro['ID'], remote_lun_id
|
||||
except Exception as err:
|
||||
LOG.info(_LI("Hypermetro id: %(metro_id)s. "
|
||||
"Remote lun id: %(remote_lun_id)s."),
|
||||
{'metro_id': hypermetro['ID'],
|
||||
'remote_lun_id': remote_lun_id})
|
||||
|
||||
return {'hypermetro_id': hypermetro['ID'],
|
||||
'remote_lun_id': remote_lun_id}
|
||||
except exception.VolumeBackendAPIException as err:
|
||||
self.rmt_client.delete_lun(remote_lun_id)
|
||||
msg = _('Create hypermetro error. %s.') % err
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
except exception.VolumeBackendAPIException:
|
||||
raise
|
||||
except Exception as err:
|
||||
msg = _("Create remote LUN error. %s.") % err
|
||||
LOG.exception(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
finally:
|
||||
self.rmt_client.logout()
|
||||
|
||||
def delete_hypermetro(self, volume):
|
||||
"""Delete hypermetro."""
|
||||
@ -96,21 +88,8 @@ class HuaweiHyperMetro(object):
|
||||
self.client.delete_hypermetro(metro_id)
|
||||
|
||||
# Delete remote lun.
|
||||
if remote_lun_id:
|
||||
metro_devices = self.configuration.hypermetro_devices
|
||||
device_info = huawei_utils.get_remote_device_info(metro_devices)
|
||||
self.rmt_client = rest_client.RestClient(self.configuration)
|
||||
self.rmt_client.login_with_ip(device_info)
|
||||
|
||||
try:
|
||||
if self.rmt_client.check_lun_exist(remote_lun_id):
|
||||
self.rmt_client.delete_lun(remote_lun_id)
|
||||
except Exception as err:
|
||||
msg = _("Delete remote lun err. %s.") % err
|
||||
LOG.exception(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
finally:
|
||||
self.rmt_client.logout()
|
||||
if remote_lun_id and self.rmt_client.check_lun_exist(remote_lun_id):
|
||||
self.rmt_client.delete_lun(remote_lun_id)
|
||||
|
||||
def _create_hypermetro_pair(self, domain_id, lun_id, remote_lun_id):
|
||||
"""Create a HyperMetroPair."""
|
||||
@ -126,78 +105,64 @@ class HuaweiHyperMetro(object):
|
||||
|
||||
def connect_volume_fc(self, volume, connector):
|
||||
"""Create map between a volume and a host for FC."""
|
||||
self.xml_file_path = self.configuration.cinder_huawei_conf_file
|
||||
metro_devices = self.configuration.hypermetro_devices
|
||||
device_info = huawei_utils.get_remote_device_info(metro_devices)
|
||||
self.rmt_client = rest_client.RestClient(self.configuration)
|
||||
self.rmt_client.login_with_ip(device_info)
|
||||
wwns = connector['wwpns']
|
||||
volume_name = huawei_utils.encode_name(volume['id'])
|
||||
|
||||
try:
|
||||
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})
|
||||
|
||||
LOG.info(_LI(
|
||||
'initialize_connection_fc, initiator: %(wwpns)s,'
|
||||
' volume name: %(volume)s.'),
|
||||
{'wwpns': wwns,
|
||||
'volume': volume_name})
|
||||
metadata = huawei_utils.get_volume_metadata(volume)
|
||||
lun_id = metadata['remote_lun_id']
|
||||
|
||||
metadata = huawei_utils.get_volume_metadata(volume)
|
||||
lun_id = metadata['remote_lun_id']
|
||||
if lun_id is None:
|
||||
lun_id = self.rmt_client.get_lun_id_by_name(volume_name)
|
||||
if lun_id is None:
|
||||
msg = _("Can't get volume id. Volume name: %s.") % volume_name
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
if lun_id is None:
|
||||
lun_id = self.rmt_client.get_volume_by_name(volume_name)
|
||||
if lun_id is None:
|
||||
msg = _("Can't get volume id. Volume name: %s.") % volume_name
|
||||
original_host_name = connector['host']
|
||||
host_name = huawei_utils.encode_host_name(original_host_name)
|
||||
host_id = self.client.add_host_with_check(host_name,
|
||||
original_host_name)
|
||||
|
||||
# Create hostgroup if not exist.
|
||||
host_id = self.rmt_client.add_host_with_check(
|
||||
host_name, original_host_name)
|
||||
|
||||
online_wwns_in_host = (
|
||||
self.rmt_client.get_host_online_fc_initiators(host_id))
|
||||
online_free_wwns = self.rmt_client.get_online_free_wwns()
|
||||
for wwn in wwns:
|
||||
if (wwn not in online_wwns_in_host
|
||||
and wwn not in online_free_wwns):
|
||||
wwns_in_host = (
|
||||
self.rmt_client.get_host_fc_initiators(host_id))
|
||||
iqns_in_host = (
|
||||
self.rmt_client.get_host_iscsi_initiators(host_id))
|
||||
if not (wwns_in_host or iqns_in_host):
|
||||
self.rmt_client.remove_host(host_id)
|
||||
|
||||
msg = _('Can not add FC port to host.')
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
host_name_before_hash = None
|
||||
host_name = connector['host']
|
||||
if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
|
||||
host_name_before_hash = host_name
|
||||
host_name = six.text_type(hash(host_name))
|
||||
for wwn in wwns:
|
||||
if wwn in online_free_wwns:
|
||||
self.rmt_client.add_fc_port_to_host(host_id, wwn)
|
||||
|
||||
# Create hostgroup if not exist.
|
||||
host_id = self.rmt_client.add_host_with_check(
|
||||
host_name, host_name_before_hash)
|
||||
(tgt_port_wwns, init_targ_map) = (
|
||||
self.rmt_client.get_init_targ_map(wwns))
|
||||
|
||||
online_wwns_in_host = (
|
||||
self.rmt_client.get_host_online_fc_initiators(host_id))
|
||||
online_free_wwns = self.rmt_client.get_online_free_wwns()
|
||||
for wwn in wwns:
|
||||
if (wwn not in online_wwns_in_host
|
||||
and wwn not in online_free_wwns):
|
||||
wwns_in_host = (
|
||||
self.rmt_client.get_host_fc_initiators(host_id))
|
||||
iqns_in_host = (
|
||||
self.rmt_client.get_host_iscsi_initiators(host_id))
|
||||
if not wwns_in_host and not iqns_in_host:
|
||||
self.rmt_client.remove_host(host_id)
|
||||
|
||||
msg = _('Can not add FC port to host.')
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
for wwn in wwns:
|
||||
if wwn in online_free_wwns:
|
||||
self.rmt_client.add_fc_port_to_host(host_id, wwn)
|
||||
|
||||
(tgt_port_wwns, init_targ_map) = (
|
||||
self.rmt_client.get_init_targ_map(wwns))
|
||||
|
||||
# Add host into hostgroup.
|
||||
hostgroup_id = self.rmt_client.add_host_into_hostgroup(host_id)
|
||||
map_info = self.rmt_client.do_mapping(lun_id,
|
||||
hostgroup_id,
|
||||
host_id)
|
||||
host_lun_id = self.rmt_client.find_host_lun_id(host_id, lun_id)
|
||||
except exception.VolumeBackendAPIException:
|
||||
raise
|
||||
except Exception as err:
|
||||
msg = _("Connect volume fc: connect volume error. %s.") % err
|
||||
LOG.exception(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
# Add host into hostgroup.
|
||||
hostgroup_id = self.rmt_client.add_host_to_hostgroup(host_id)
|
||||
map_info = self.rmt_client.do_mapping(lun_id,
|
||||
hostgroup_id,
|
||||
host_id)
|
||||
host_lun_id = self.rmt_client.get_host_lun_id(host_id, lun_id)
|
||||
|
||||
# Return FC properties.
|
||||
fc_info = {'driver_volume_type': 'fibre_channel',
|
||||
@ -216,76 +181,61 @@ class HuaweiHyperMetro(object):
|
||||
def disconnect_volume_fc(self, volume, connector):
|
||||
"""Delete map between a volume and a host for FC."""
|
||||
# Login remote storage device.
|
||||
self.xml_file_path = self.configuration.cinder_huawei_conf_file
|
||||
metro_devices = self.configuration.hypermetro_devices
|
||||
device_info = huawei_utils.get_remote_device_info(metro_devices)
|
||||
self.rmt_client = rest_client.RestClient(self.configuration)
|
||||
self.rmt_client.login_with_ip(device_info)
|
||||
|
||||
try:
|
||||
wwns = connector['wwpns']
|
||||
volume_name = huawei_utils.encode_name(volume['id'])
|
||||
metadata = huawei_utils.get_volume_metadata(volume)
|
||||
lun_id = metadata['remote_lun_id']
|
||||
host_name = connector['host']
|
||||
left_lunnum = -1
|
||||
lungroup_id = None
|
||||
view_id = None
|
||||
wwns = connector['wwpns']
|
||||
volume_name = huawei_utils.encode_name(volume['id'])
|
||||
metadata = huawei_utils.get_volume_metadata(volume)
|
||||
lun_id = metadata['remote_lun_id']
|
||||
host_name = connector['host']
|
||||
left_lunnum = -1
|
||||
lungroup_id = None
|
||||
view_id = None
|
||||
|
||||
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},)
|
||||
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 host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
|
||||
host_name = six.text_type(hash(host_name))
|
||||
host_name = huawei_utils.encode_host_name(host_name)
|
||||
hostid = self.rmt_client.get_host_id_by_name(host_name)
|
||||
if hostid:
|
||||
mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
|
||||
view_id = self.rmt_client.find_mapping_view(
|
||||
mapping_view_name)
|
||||
if view_id:
|
||||
lungroup_id = self.rmt_client.find_lungroup_from_map(
|
||||
view_id)
|
||||
|
||||
hostid = self.rmt_client.find_host(host_name)
|
||||
if hostid:
|
||||
mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
|
||||
view_id = self.rmt_client.find_mapping_view(
|
||||
mapping_view_name)
|
||||
if view_id:
|
||||
lungroup_id = self.rmt_client.find_lungroup_from_map(
|
||||
view_id)
|
||||
|
||||
if lun_id and self.rmt_client.check_lun_exist(lun_id):
|
||||
if lungroup_id:
|
||||
lungroup_ids = self.rmt_client.get_lungroupids_by_lunid(
|
||||
lun_id)
|
||||
if lungroup_id in lungroup_ids:
|
||||
self.rmt_client.remove_lun_from_lungroup(
|
||||
lungroup_id, lun_id)
|
||||
else:
|
||||
LOG.warning(_LW("Lun is not in lungroup. "
|
||||
"Lun id: %(lun_id)s, "
|
||||
"lungroup id: %(lungroup_id)s"),
|
||||
{"lun_id": lun_id,
|
||||
"lungroup_id": lungroup_id})
|
||||
|
||||
(tgt_port_wwns, init_targ_map) = (
|
||||
self.rmt_client.get_init_targ_map(wwns))
|
||||
|
||||
hostid = self.rmt_client.find_host(host_name)
|
||||
if hostid:
|
||||
mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
|
||||
view_id = self.rmt_client.find_mapping_view(
|
||||
mapping_view_name)
|
||||
if view_id:
|
||||
lungroup_id = self.rmt_client.find_lungroup_from_map(
|
||||
view_id)
|
||||
if lun_id and self.rmt_client.check_lun_exist(lun_id):
|
||||
if lungroup_id:
|
||||
left_lunnum = self.rmt_client.get_lunnum_from_lungroup(
|
||||
lungroup_id)
|
||||
lungroup_ids = self.rmt_client.get_lungroupids_by_lunid(
|
||||
lun_id)
|
||||
if lungroup_id in lungroup_ids:
|
||||
self.rmt_client.remove_lun_from_lungroup(
|
||||
lungroup_id, lun_id)
|
||||
else:
|
||||
LOG.warning(_LW("Lun is not in lungroup. "
|
||||
"Lun id: %(lun_id)s, "
|
||||
"lungroup id: %(lungroup_id)s"),
|
||||
{"lun_id": lun_id,
|
||||
"lungroup_id": lungroup_id})
|
||||
|
||||
except Exception as err:
|
||||
msg = _("Remote detatch volume error. %s.") % err
|
||||
LOG.exception(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
finally:
|
||||
self.rmt_client.logout()
|
||||
(tgt_port_wwns, init_targ_map) = (
|
||||
self.rmt_client.get_init_targ_map(wwns))
|
||||
|
||||
hostid = self.rmt_client.get_host_id_by_name(host_name)
|
||||
if hostid:
|
||||
mapping_view_name = constants.MAPPING_VIEW_PREFIX + hostid
|
||||
view_id = self.rmt_client.find_mapping_view(
|
||||
mapping_view_name)
|
||||
if view_id:
|
||||
lungroup_id = self.rmt_client.find_lungroup_from_map(
|
||||
view_id)
|
||||
if lungroup_id:
|
||||
left_lunnum = self.rmt_client.get_lunnum_from_lungroup(
|
||||
lungroup_id)
|
||||
|
||||
if int(left_lunnum) > 0:
|
||||
info = {'driver_volume_type': 'fibre_channel',
|
||||
@ -298,9 +248,7 @@ class HuaweiHyperMetro(object):
|
||||
return info
|
||||
|
||||
def _wait_volume_ready(self, lun_id):
|
||||
event_type = 'LUNReadyWaitInterval'
|
||||
wait_interval = huawei_utils.get_wait_interval(self.xml_file_path,
|
||||
event_type)
|
||||
wait_interval = self.configuration.lun_ready_wait_interval
|
||||
|
||||
def _volume_ready():
|
||||
result = self.rmt_client.get_lun_info(lun_id)
|
||||
@ -309,8 +257,7 @@ class HuaweiHyperMetro(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
huawei_utils.wait_for_condition(self.xml_file_path,
|
||||
_volume_ready,
|
||||
huawei_utils.wait_for_condition(_volume_ready,
|
||||
wait_interval,
|
||||
wait_interval * 10)
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015 Huawei Technologies Co., Ltd.
|
||||
# Copyright (c) 2016 Huawei Technologies Co., Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -16,10 +16,11 @@
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.i18n import _, _LI
|
||||
from cinder.volume.drivers.huawei import constants
|
||||
from cinder.volume.drivers.huawei import huawei_utils
|
||||
from cinder.volume import qos_specs
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -28,11 +29,52 @@ class SmartQos(object):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
def create_qos(self, qos, lun_id):
|
||||
@staticmethod
|
||||
def get_qos_by_volume_type(volume_type):
|
||||
# We prefer the qos_specs association
|
||||
# and override any existing extra-specs settings
|
||||
# if present.
|
||||
if not volume_type:
|
||||
return {}
|
||||
|
||||
qos_specs_id = volume_type.get('qos_specs_id')
|
||||
if not qos_specs_id:
|
||||
return {}
|
||||
|
||||
qos = {}
|
||||
ctxt = context.get_admin_context()
|
||||
kvs = qos_specs.get_qos_specs(ctxt, qos_specs_id)['specs']
|
||||
LOG.info(_LI('The QoS sepcs is: %s.'), kvs)
|
||||
for k, v in kvs.items():
|
||||
if k not in constants.HUAWEI_VALID_KEYS:
|
||||
continue
|
||||
|
||||
if k.upper() != 'IOTYPE' and int(v) <= 0:
|
||||
msg = _('QoS config is wrong. %s must > 0.') % k
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
elif k.upper() == 'IOTYPE' and v not in ['0', '1', '2']:
|
||||
msg = _('Illegal value specified for IOTYPE: 0, 1, or 2.')
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
else:
|
||||
qos[k.upper()] = v
|
||||
|
||||
return qos
|
||||
|
||||
def _is_high_priority(self, qos):
|
||||
"""Check QoS priority."""
|
||||
for key, value in qos.items():
|
||||
if (key.find('MIN') == 0) or (key.find('LATENCY') == 0):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def add(self, qos, lun_id):
|
||||
policy_id = None
|
||||
try:
|
||||
# Check QoS priority.
|
||||
if huawei_utils.check_qos_high_priority(qos):
|
||||
if self._is_high_priority(qos):
|
||||
self.client.change_lun_priority(lun_id)
|
||||
# Create QoS policy and activate it.
|
||||
version = self.client.find_array_version()
|
||||
@ -51,13 +93,17 @@ class SmartQos(object):
|
||||
if policy_id is not None:
|
||||
self.client.delete_qos_policy(policy_id)
|
||||
|
||||
def delete_qos(self, qos_id):
|
||||
def remove(self, qos_id, lun_id):
|
||||
qos_info = self.client.get_qos_info(qos_id)
|
||||
qos_status = qos_info['RUNNINGSTATUS']
|
||||
# 2: Active status.
|
||||
if qos_status == constants.STATUS_QOS_ACTIVE:
|
||||
self.client.activate_deactivate_qos(qos_id, False)
|
||||
self.client.delete_qos_policy(qos_id)
|
||||
lun_list = self.client.get_lun_list_in_qos(qos_id, qos_info)
|
||||
if len(lun_list) <= 1:
|
||||
qos_status = qos_info['RUNNINGSTATUS']
|
||||
# 2: Active status.
|
||||
if qos_status == constants.STATUS_QOS_ACTIVE:
|
||||
self.client.activate_deactivate_qos(qos_id, False)
|
||||
self.client.delete_qos_policy(qos_id)
|
||||
else:
|
||||
self.client.remove_lun_from_qos(lun_id, lun_list, qos_id)
|
||||
|
||||
|
||||
class SmartPartition(object):
|
||||
@ -132,9 +178,9 @@ class SmartX(object):
|
||||
reason=(_('Illegal value specified for thin: '
|
||||
'Can not set thin and thick at the same time.')))
|
||||
else:
|
||||
opts['LUNType'] = 1
|
||||
opts['LUNType'] = constants.THIN_LUNTYPE
|
||||
if opts['thick_provisioning_support'] == 'true':
|
||||
opts['LUNType'] = 0
|
||||
opts['LUNType'] = constants.THICK_LUNTYPE
|
||||
|
||||
return opts
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user