cinder/cinder/volume/drivers/zte/zte_ks.py

995 lines
42 KiB
Python

# Copyright 2016 ZTE Corporation. All rights reserved
# 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 driver for ZTE storage systems.
"""
import hashlib
import json
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import units
import six
from six.moves import urllib
from cinder import exception
from cinder.i18n import _
from cinder import interface
from cinder import utils
from cinder.volume import configuration
from cinder.volume import driver
from cinder.volume.drivers.zte import zte_pub
LOG = logging.getLogger(__name__)
zte_opts = [
cfg.IPOpt('zteControllerIP0', default=None,
help='Main controller IP.'),
cfg.IPOpt('zteControllerIP1', default=None,
help='Slave controller IP.'),
cfg.IPOpt('zteLocalIP', default=None, help='Local IP.'),
cfg.StrOpt('zteUserName', default=None, help='User name.'),
cfg.StrOpt('zteUserPassword', default=None, secret=True,
help='User password.'),
cfg.IntOpt('zteChunkSize', default=4,
help='Virtual block size of pool. '
'Unit : KB. '
'Valid value : 4, 8, 16, 32, 64, 128, 256, 512. '),
cfg.IntOpt('zteAheadReadSize', default=8, help='Cache readahead size.'),
cfg.IntOpt('zteCachePolicy', default=1,
help='Cache policy. '
'0, Write Back; 1, Write Through.'),
cfg.IntOpt('zteSSDCacheSwitch', default=1,
help='SSD cache switch. '
'0, OFF; 1, ON.'),
cfg.ListOpt('zteStoragePool', default=[], help='Pool name list.'),
cfg.IntOpt('ztePoolVoAllocatedPolicy', default=0,
help='Pool volume allocated policy. '
'0, Auto; '
'1, High Performance Tier First; '
'2, Performance Tier First; '
'3, Capacity Tier First.'),
cfg.IntOpt('ztePoolVolMovePolicy', default=0,
help='Pool volume move policy.'
'0, Auto; '
'1, Highest Available; '
'2, Lowest Available; '
'3, No Relocation.'),
cfg.BoolOpt('ztePoolVolIsThin', default=False,
help='Whether it is a thin volume.'),
cfg.IntOpt('ztePoolVolInitAllocatedCapacity', default=0,
help='Pool volume init allocated Capacity.'
'Unit : KB. '),
cfg.IntOpt('ztePoolVolAlarmThreshold', default=0,
help='Pool volume alarm threshold. [0, 100]'),
cfg.IntOpt('ztePoolVolAlarmStopAllocatedFlag', default=0,
help='Pool volume alarm stop allocated flag.')
]
CONF = cfg.CONF
CONF.register_opts(zte_opts, group=configuration.SHARED_CONF_GROUP)
class ZTEVolumeDriver(driver.VolumeDriver):
VERSION = "1.0.0"
# ThirdPartySystems wiki page
CI_WIKI_NAME = "ZTE_cinder2_CI"
def __init__(self, *args, **kwargs):
super(ZTEVolumeDriver, self).__init__(*args, **kwargs)
self.configuration.append_config_values(zte_opts)
self.url = ''
self.login_info = {}
self.session_id = ''
def _get_md5(self, src_string):
md5obj = hashlib.md5()
md5obj.update(src_string.encode('UTF-8'))
md5_string = md5obj.hexdigest()
md5_string = md5_string[0:19]
return md5_string
def _call_method(self, method='', params=None):
sid = self._get_sessionid()
return self._call(sid, method, params)
def _call(self, sessin_id='', method='', params=None):
try:
params = params or {}
data = ("sessionID=" + sessin_id + "&method=" +
method + "&params=" + json.dumps(params))
LOG.debug('Req Data: method %(method)s data %(data)s.',
{'method': method, 'data': data})
headers = {"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded"}
req = urllib.request.Request(self.url, data, headers)
req.get_method = lambda: 'POST'
# self.url used for req is coded to always use the HTTPS scheme
response = urllib.request.urlopen(req, # nosec
timeout=
zte_pub.ZTE_DEFAULT_TIMEOUT
).read()
LOG.debug('Response Data: method %(method)s res %(res)s.',
{'method': method, 'res': response})
except Exception:
LOG.exception('Bad response from server.')
msg = (_('_call failed.'))
raise exception.VolumeBackendAPIException(data=msg)
res_json = json.loads(response)
return res_json
def _get_server(self):
controller_ip = (self.login_info['ControllerIP0']
or self.login_info['ControllerIP1'] or '')
self.url = 'https://' + controller_ip + '/phpclient/client.php'
LOG.debug('Set ZTE server is %s.', self.url)
def _change_server(self):
if (self.login_info['ControllerIP0'] and
self.login_info['ControllerIP1']):
controller_ip = (self.login_info['ControllerIP1']
if self.login_info['ControllerIP0'] in self.url
else self.login_info['ControllerIP0'])
self.url = 'https://' + controller_ip + '/phpclient/client.php'
def _user_login(self):
loginfo = {'UserName': self.login_info['UserName'],
'UserPassword': self.login_info['UserPassword'],
'LocalIP': self.login_info['LocalIP'],
'LoginType': zte_pub.ZTE_WEB_LOGIN_TYPE}
result = self._call('""', 'plat.session.signin', loginfo)
if result['returncode'] in [zte_pub.ZTE_SUCCESS,
zte_pub.ZTE_SESSION_EXIST]:
self.session_id = result['data']['sessionID']
return self.session_id
else:
err_msg = (
_('Failed to login. Return code: %(ret)s.') % {
'ret': result['returncode']})
raise exception.VolumeBackendAPIException(
data=err_msg)
def do_setup(self, context):
"""Any initialization the volume driver does while starting."""
self.login_info = {
'ControllerIP0': self.configuration.zteControllerIP0,
'ControllerIP1': self.configuration.zteControllerIP1,
'LocalIP': self.configuration.zteLocalIP,
'UserName': self.configuration.zteUserName,
'UserPassword': self.configuration.zteUserPassword}
self._get_server()
try:
self.session_id = self._user_login()
except exception.VolumeBackendAPIException:
self._change_server()
self.session_id = self._user_login()
def check_for_setup_error(self):
zteControllerIP0 = self.configuration.zteControllerIP0
if zteControllerIP0 is None:
msg = (_("Controller IP is missing for ZTE driver."))
raise exception.VolumeBackendAPIException(data=msg)
zteUserName = self.configuration.zteUserName
if zteUserName is None:
msg = (_("User Name is missing for ZTE driver."))
raise exception.VolumeBackendAPIException(data=msg)
zteUserPassword = self.configuration.zteUserPassword
if zteUserPassword is None:
msg = (_("User Password is missing for ZTE driver."))
raise exception.VolumeBackendAPIException(data=msg)
def _get_sessionid(self):
try:
sid = self.session_id
ret = self._call(sid, 'plat.session.heartbeat')
if ret['returncode'] == zte_pub.ZTE_SUCCESS:
return sid
else:
LOG.info('heartbeat failed. Return code: %(ret)s.',
{'ret': ret['returncode']})
except Exception:
LOG.exception('_get_sessionid error.')
self._change_server()
return self._user_login()
def _get_pool_list(self):
pool_info_list = []
for pool_name in self.configuration.zteStoragePool:
if pool_name:
ret = self._call_method(
'GetPoolInfo', {
'scPoolName': pool_name})
pool_info = {'name': pool_name}
if ((ret['returncode'] == zte_pub.ZTE_SUCCESS) and
(ret['data']['sdwState'] == zte_pub.ZTE_STATUS_OK)):
total_capacity = ret['data']['qwTotalCapacity']
free_capacitity = ret['data']['qwFreeCapacity']
pool_info['total'] = (
float(total_capacity) / units.Ki)
pool_info['free'] = (
float(free_capacitity) / units.Ki)
pool_info_list.append(pool_info)
if not pool_info_list:
err_msg = (_('No pool available.'))
raise exception.VolumeBackendAPIException(data=err_msg)
return pool_info_list
def _find_pool_to_create_volume(self):
pool_list = self._get_pool_list()
pool = max(pool_list, key=lambda arg: arg['free'])
return pool['name']
def _create_volume_in_pool(self, volume_name, volume_size, pool_name):
vol = {
'scPoolName': pool_name,
'scVolName': volume_name,
'sdwStripeDepth': self.configuration.zteChunkSize,
'qwCapacity': float(volume_size),
'sdwCtrlPrefer': 0xFFFF,
'sdwCachePolicy': self.configuration.zteCachePolicy,
'sdwAheadReadSize': self.configuration.zteAheadReadSize,
'sdwAllocPolicy': self.configuration.ztePoolVoAllocatedPolicy,
'sdwMovePolicy': self.configuration.ztePoolVolMovePolicy,
'udwIsThinVol': self.configuration.ztePoolVolIsThin,
'uqwInitAllocedCapacity':
self.configuration.ztePoolVolInitAllocatedCapacity,
'sdwAlarmThreshold':
self.configuration.ztePoolVolAlarmThreshold,
'sdwAlarmStopAllocFlag':
self.configuration.ztePoolVolAlarmStopAllocatedFlag,
'dwSSDCacheSwitch': self.configuration.zteSSDCacheSwitch}
ret = self._call_method('CreateVolOnPool', vol)
if ret['returncode'] not in [zte_pub.ZTE_ERR_OBJECT_EXIST,
zte_pub.ZTE_SUCCESS]:
err_msg = (
_('Create volume failed. Volume name: %(name)s. '
'Return code: %(ret)s.') %
{'name': volume_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(
data=err_msg)
def _create_volume(self, volume_name, volume_size):
pool_name = self._find_pool_to_create_volume()
if pool_name:
self._create_volume_in_pool(volume_name, volume_size, pool_name)
else:
msg = _('No pool available.')
raise exception.VolumeDriverException(message=msg)
def create_volume(self, volume):
"""Create a new volume."""
volume_name = self._translate_volume_name(volume['name'])
volume_size = float(volume['size'] * units.Mi)
self._create_volume(volume_name, volume_size)
def _delete_clone_volume(self, cloned_name):
cloned_name += zte_pub.ZTE_CLONE_SUFFIX
cvol_name = {'scCvolName': cloned_name}
ret = self._call_method('DelCvol', cvol_name)
if ret['returncode'] not in [zte_pub.ZTE_ERR_CLONE_OR_SNAP_NOT_EXIST,
zte_pub.ZTE_ERR_VAS_OBJECT_NOT_EXIST,
zte_pub.ZTE_SUCCESS]:
err_msg = (_('Delete volume failed. Clone name: %(name)s. '
'Return code: %(ret)s.') %
{'name': cloned_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
def _remove_volume_from_group(self, volume):
ret = self._call_method('GetGrpNamesOfVol', {'cVolName': volume})
if ret['returncode'] == zte_pub.ZTE_SUCCESS:
group_num = int(ret['data']['sdwMapGrpNum'])
for index in range(0, group_num):
group_name = ret['data']['cMapGrpNames'][index]
lun_ID = ret['data']['sdwLunLocalId'][index]
self._map_delete_lun(lun_ID, group_name)
def _delete_volume(self, volume_name):
vol_name = {'cVolName': volume_name}
ret = self._call_method('DelVol', vol_name)
if ret['returncode'] not in [zte_pub.ZTE_ERR_VOLUME_NOT_EXIST,
zte_pub.ZTE_ERR_LUNDEV_NOT_EXIST,
zte_pub.ZTE_SUCCESS]:
err_msg = (_('Delete volume failed. Volume name: %(name)s.'
'Return code: %(ret)s.') %
{'name': volume_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
def delete_volume(self, volume):
"""Delete a volume."""
volume_name = self._translate_volume_name(volume['name'])
LOG.debug('delete_volume: volume name: %s.', volume_name)
self._delete_clone_relation_by_volname(volume_name, False)
self._remove_volume_from_group(volume_name)
self._delete_volume(volume_name)
def _delete_cvol(self, cloned_name, issnapshot):
cvol_name = {'scCvolName': cloned_name}
ret = self._call_method('SyncForceDelCvol', cvol_name)
if ret['returncode'] not in [zte_pub.ZTE_ERR_CLONE_OR_SNAP_NOT_EXIST,
zte_pub.ZTE_ERR_VAS_OBJECT_NOT_EXIST,
zte_pub.ZTE_SUCCESS]:
err_msg = (_('_delete_cvol: Failed to delete clone vol. '
'cloned name: %(name)s with Return code: '
'%(ret)s.') %
{'name': cloned_name, 'ret': ret['returncode']})
if ret['returncode'] == zte_pub.ZTE_VOLUME_TASK_NOT_FINISHED:
if issnapshot:
raise exception.SnapshotIsBusy(snapshot_name=cloned_name)
else:
raise exception.VolumeIsBusy(volume_name=cloned_name)
else:
raise exception.VolumeBackendAPIException(data=err_msg)
def _delete_clone_relation_by_volname(self, volname, issnapshot):
svol_name = {'scVolName': volname}
LOG.debug('GetCvolNamesOnVol: volume name: %s.', volname)
ret = self._call_method('GetCvolNamesOnVol', svol_name)
data_info = ret['data']
if ret['returncode'] == zte_pub.ZTE_SUCCESS:
sccvolnames = data_info['scCvolNames']
for i in range(0, ret['data']['sdwCvolNum']):
cloned_name = sccvolnames[i]['scCvolName']
self._delete_cvol(cloned_name, issnapshot)
cloned_name = volname + zte_pub.ZTE_CLONE_SUFFIX
self._delete_cvol(cloned_name, False)
def _create_snapshot(
self,
snapshot_name,
src_vol,
src_vol_size,
snapshot_mode):
svol_paras = {
'scVolName': src_vol,
'scSnapName': snapshot_name,
'sdwSnapType': 1,
'swRepoSpaceAlarm': 60,
'swRepoOverflowPolicy': 0,
'sqwRepoCapacity': float(src_vol_size * units.Mi),
'ucIsAgent': 0,
'ucSnapMode': snapshot_mode,
'is_private': 0,
'ucIsAuto': 0}
ret = self._call_method('CreateSvol', svol_paras)
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('Failed to create snap.snap name: %(snapname)s,'
'srvol name :%(srv)s with Return code: %(ret)s. ') %
{'snapname': snapshot_name,
'srv': src_vol,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
def create_snapshot(self, snapshot):
"""create a snapshot from volume"""
snapshot_name = self._translate_volume_name(snapshot['name'])
volume_name = self._translate_volume_name(snapshot['volume_name'])
volume_size = snapshot['volume_size']
self._create_snapshot(snapshot_name,
volume_name,
volume_size,
zte_pub.ZTE_SNAPSHOT_MODE_RW)
def _delete_snapshot(self, snapshot_name):
svol_name = {'scSnapName': snapshot_name}
ret = self._call_method('DelSvol', svol_name)
if ret['returncode'] not in [zte_pub.ZTE_ERR_CLONE_OR_SNAP_NOT_EXIST,
zte_pub.ZTE_ERR_VAS_OBJECT_NOT_EXIST,
zte_pub.ZTE_SUCCESS]:
err_msg = (_('_delete_snapshot:Failed to delete snap.'
'snap name: %(snapname)s with Return code: '
'%(ret)s.') %
{'snapname': snapshot_name,
'ret': ret['returncode']})
if ret['returncode'] == zte_pub.ZTE_ERR_SNAP_EXIST_CLONE:
raise exception.SnapshotIsBusy(snapshot_name=snapshot_name)
else:
raise exception.VolumeBackendAPIException(data=err_msg)
def delete_snapshot(self, snapshot):
"""delete a snapshot volume"""
snapshot_name = self._translate_volume_name(snapshot['name'])
self._delete_clone_relation_by_volname(snapshot_name, True)
self._delete_snapshot(snapshot_name)
def _extend_volume(self, volume_name, inc_size):
ext_vol_paras = {'scVolName': volume_name,
'qwExpandCapacity':
float(inc_size * units.Ki)}
ret = self._call_method('ExpandVolOnPool', ext_vol_paras)
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('_extend_volume:Failed to extend vol.vol name:'
'%(name)s with Return code: %(ret)s.') %
{'name': volume_name, 'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
def extend_volume(self, volume, new_size):
"""extend volume size"""
size_increase = (int(new_size)) - volume['size']
volume_name = self._translate_volume_name(volume['name'])
self._extend_volume(volume_name, size_increase)
def _cloned_volume(self, cloned_name, src_name, vol_size, vol_type):
self._create_volume(cloned_name, vol_size)
cvol_paras = {
'scCvolName': cloned_name + zte_pub.ZTE_CLONE_SUFFIX,
'scBvolName': src_name,
'scTargetName': cloned_name,
'sdwInitSync': 1,
'sdwProtectRestore': 0,
'sdwPri': 0,
'sdwPolicy': 0,
'sdwBvolType': vol_type}
ret = self._call_method('CreateCvol', cvol_paras)
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
self._delete_volume(cloned_name)
err_msg = (_('_cloned_volume: Failed to clone vol. '
'vol name: %(name)s with Return code: %(ret)s. ') %
{'name': src_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
def create_cloned_volume(self, volume, src_vref):
"""clone a volume"""
bvol_name = self._translate_volume_name(src_vref['name'])
cvol_name = self._translate_volume_name(volume['name'])
if volume['size'] < src_vref['size']:
err_msg = (_('Cloned volume size invalid. '
'Clone size: %(cloned_size)s. '
'Src volume size: %(volume_size)s.') %
{'cloned_size': volume['size'],
'volume_size': src_vref['size']})
raise exception.VolumeDriverException(message=err_msg)
else:
volume_size = float(
volume['size'] * units.Mi)
try:
self._cloned_volume(
cvol_name,
bvol_name,
volume_size,
zte_pub.ZTE_VOLUME)
except Exception:
self._delete_clone_relation_by_volname(bvol_name, False)
self._cloned_volume(
cvol_name,
bvol_name,
volume_size,
zte_pub.ZTE_VOLUME)
def create_volume_from_snapshot(self, volume, snapshot):
"""Create volume from snapshot """
bvol_name = self._translate_volume_name(snapshot['name'])
cvol_name = self._translate_volume_name(volume['name'])
if volume['size'] < snapshot['volume_size']:
err_msg = (
_('Cloned volume size invalid. '
'Clone size: %(cloned_size)s. '
'Src volume size: %(volume_size)s.') %
{'cloned_size': volume['size'],
'volume_size': snapshot['volume_size']})
raise exception.VolumeDriverException(message=err_msg)
else:
volume_size = float(
volume['size'] * units.Mi)
try:
self._cloned_volume(
cvol_name,
bvol_name,
volume_size,
zte_pub.ZTE_SNAPSHOT)
except Exception:
self._delete_clone_relation_by_volname(bvol_name, False)
self._cloned_volume(
cvol_name,
bvol_name,
volume_size,
zte_pub.ZTE_SNAPSHOT)
def create_export(self, context, volume, connector):
"""Exports the volume """
pass
def ensure_export(self, context, volume):
"""Driver entry point to get the export info for an existing volume."""
pass
def remove_export(self, context, volume_id):
"""Driver entry point to remove an export for a volume."""
pass
def get_volume_stats(self, refresh=False):
"""Get volume status."""
if refresh:
self._update_volume_status()
return self._stats
def _translate_host_name(self, host_name):
new_name = 'host_' + six.text_type(self._get_md5(host_name))
new_name = new_name.replace('-', 'R')
LOG.debug('_translate_host_name: Name in cinder: %(old)s, '
'new name in storage system: %(new)s.',
{'old': host_name, 'new': new_name})
return new_name
def _translate_volume_name(self, vol_name):
new_name = zte_pub.ZTE_VOL_NAME_PREFIX_NEW + six.text_type(
self._get_md5(vol_name))
new_name = new_name.replace('-', 'R')
LOG.debug('_translate_volume_name: Name in cinder: %(old)s, '
'new name in storage system: %(new)s.',
{'old': vol_name, 'new': new_name})
return new_name
def _get_lunid_from_vol(self, volume_name, map_group_name):
map_grp_info = {'cMapGrpName': map_group_name}
ret = self._call_method('GetMapGrpInfo', map_grp_info)
if ret['returncode'] == zte_pub.ZTE_SUCCESS:
lun_num = int(ret['data']['sdwLunNum'])
lun_info = ret['data']['tLunInfo']
for count in range(0, lun_num):
if volume_name == lun_info[count]['cVolName']:
return lun_info[count]['sdwLunId']
return None
elif ret['returncode'] == zte_pub.ZTE_ERR_GROUP_NOT_EXIST:
return None
else:
err_msg = (_('_get_lunid_from_vol:Get lunid from vol fail. '
'Group name:%(name)s vol:%(vol)s '
'with Return code: %(ret)s.') %
{'name': map_group_name,
'vol': volume_name,
'ret': ret['returncode']})
raise exception.VolumeDriverException(message=err_msg)
def _get_group_lunnum(self, map_group_name):
map_grp_info = {'cMapGrpName': map_group_name}
ret = self._call_method('GetMapGrpInfo', map_grp_info)
if ret['returncode'] == zte_pub.ZTE_SUCCESS:
lun_num = ret['data']['sdwLunNum']
return int(lun_num)
elif ret['returncode'] == zte_pub.ZTE_ERR_GROUP_NOT_EXIST:
return -1
else:
err_msg = (_('_get_group_lunnum:Get group info fail. '
'Group name:%(name)s with Return code: %(ret)s.') %
{'name': map_group_name, 'ret': ret['returncode']})
raise exception.VolumeDriverException(message=err_msg)
def _delete_group(self, map_group_name):
# before delete the group, we must delete the hosts in group
self._map_delete_host(map_group_name)
map_grp_info = {'cMapGrpName': map_group_name}
ret = self._call_method('DelMapGrp', map_grp_info)
if ret['returncode'] not in [zte_pub.ZTE_SUCCESS,
zte_pub.ZTE_ERR_GROUP_NOT_EXIST]:
err_msg = (_('_delete_group:Del group fail. '
'Group name:%(name)s with Return code: %(ret)s.') %
{'name': map_group_name, 'ret': ret['returncode']})
raise exception.VolumeDriverException(message=err_msg)
def _map_add_lun(self, volume_name, map_group_name):
add_vol_to_grp = {
'cMapGrpName': map_group_name,
'sdwLunId': 0,
'cVolName': volume_name}
ret = self._call_method('AddVolToGrp', add_vol_to_grp)
if ret['returncode'] in [zte_pub.ZTE_SUCCESS,
zte_pub.ZTE_VOLUME_IN_GROUP,
zte_pub.ZTE_ERR_VOL_EXISTS]:
return self._get_lunid_from_vol(volume_name, map_group_name)
err_msg = (
_(
'_map_add_lun:fail to add vol to grp. group name:%(name)s'
' lunid:%(lun)s '
'vol:%(vol)s with Return code: %(ret)s') %
{'name': map_group_name,
'lun': 0,
'vol': volume_name,
'ret': ret['returncode']})
raise exception.VolumeDriverException(message=err_msg)
def _update_volume_group_info(self):
pool_list = self._get_pool_list()
pool_info = {'total': 0, 'free': 0}
for item in pool_list:
pool_info['total'] += item['total']
pool_info['free'] += item['free']
return pool_info
def _get_sysinfo(self):
ret = self._call_method('GetSysInfo')
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('_get_sysinfo:get sys info failed. Return code: '
'%(ret)s.'),
{'ret': ret['returncode']})
raise exception.VolumeDriverException(message=err_msg)
return ret['data']
def _update_volume_status(self):
LOG.debug("Updating volume status")
sys_info = self._get_sysinfo()
backend_name = self.configuration.safe_get('volume_backend_name')
pool_info = self._update_volume_group_info()
data = {
"volume_backend_name": backend_name or 'ZteISCSIDriver',
'vendor_name': sys_info['cVendor'],
'driver_version': sys_info['cVersionName'],
'storage_protocol': 'iSCSI',
'multiattach': False,
'total_capacity_gb': pool_info['total'],
'free_capacity_gb': pool_info['free'],
'reserved_percentage': 0,
'QoS_support': False}
self._stats = data
def _create_group(self, initiator_name, map_group_name):
pass
def _map_delete_host(self, map_group_name):
pass
def _map_delete_lun(self, lunid, initiator_name):
pass
@interface.volumedriver
class ZteISCSIDriver(ZTEVolumeDriver, driver.ISCSIDriver):
"""Zte iSCSI volume driver."""
# ThirdPartySystems wiki page
WIKI_CI_NAME = "ZTE_cinder2_CI"
# TODO(smcginnis) Remove driver in Queens if CI issues not fixed
SUPPORTED = False
def __init__(self, *args, **kwargs):
super(ZteISCSIDriver, self).__init__(*args, **kwargs)
def _map_lun(self, initiator_name, volume_name, map_group_name):
self._create_group(initiator_name, map_group_name)
return self._map_add_lun(volume_name, map_group_name)
def _get_net_cfg_ips(self):
ret = self._call_method('GetSystemNetCfg')
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('get_Net_Cfg failed. Return code: %(ret)s.') %
{'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
targetips = []
net_cfg_info = ret['data']['tSystemNetCfg']
for item in range(ret['data']['sdwDeviceNum']):
if (net_cfg_info[item]['udwRoleType'] == 0 and
net_cfg_info[item]['cIpAddr']):
targetips.append(net_cfg_info[item]['cIpAddr'])
return targetips
@utils.synchronized('zte_locked_initialize_connection')
def initialize_connection(self, volume, connector):
"""Map a volume to a host and return target iSCSI information."""
initiator_name = connector['initiator']
volume_name = self._translate_volume_name(volume['name'])
LOG.debug('initialize_connection: Volume name: %(volume)s. '
'Initiator name: %(ini)s.',
{'volume': volume_name,
'ini': initiator_name})
iscsi_conf = self._get_iscsi_info()
target_ips = iscsi_conf['DefaultTargetIPs']
target_portals = {}
target_iqns = []
for ip in target_ips:
iqn = self._get_tgt_iqn(ip)
if iqn:
if iqn not in target_iqns:
target_iqns.append(iqn)
target_portals[iqn] = ['%s:%s' % (ip, '3260')]
else:
target_portals[iqn].append('%s:%s' % (ip, '3260'))
if not target_iqns:
msg = (_('Failed to get target ip or iqn '
'for initiator %(ini)s, please check config file.') %
{'ini': initiator_name})
raise exception.VolumeDriverException(message=msg)
map_group_name = self._translate_grp_name(initiator_name)
lunid = self._map_lun(initiator_name, volume_name, map_group_name)
# Return iSCSI properties.
properties = {
'target_discovered': False,
'target_portal': target_portals[
target_iqns[0]][0],
'target_iqn': target_iqns[0],
'target_lun': lunid,
'volume_id': volume['id']}
if target_iqns and target_portals:
properties['target_portals'] = target_portals
properties['target_iqns'] = target_iqns
return {'driver_volume_type': 'iscsi', 'data': properties}
@utils.synchronized('zte_locked_initialize_connection')
def terminate_connection(self, volume, connector, **kwargs):
"""Delete map between a volume and a host."""
initiator_name = connector['initiator']
volume_name = self._translate_volume_name(volume['name'])
LOG.debug('volume name: %(volume)s, initiator name: %(ini)s.',
{'volume': volume_name,
'ini': initiator_name})
map_group_name = self._translate_grp_name(initiator_name)
lunid = self._get_lunid_from_vol(volume_name, map_group_name)
self._map_delete_lun(lunid, initiator_name)
def _get_iscsi_info(self):
iscsi_info = {}
try:
iscsi_info['DefaultTargetIPs'] = self._get_net_cfg_ips()
if not iscsi_info['DefaultTargetIPs']:
err_msg = _('Can not get target ip address. ')
raise exception.VolumeBackendAPIException(data=err_msg)
initiator_list = []
iscsi_info['Initiator'] = initiator_list
except Exception:
LOG.exception('_get_iscsi_info error.')
raise
return iscsi_info
def _translate_grp_name(self, grp_name):
new_name = zte_pub.ZTE_HOST_GROUP_NAME_PREFIX + six.text_type(
self._get_md5(grp_name))
new_name = new_name.replace('-', 'R')
LOG.debug('_translate_grp_name:Name in cinder: %(old)s, '
'new name in storage system: %(new)s.',
{'old': grp_name,
'new': new_name})
return new_name
def _get_target_ip_ctrl(self, target_ip):
LOG.debug('_get_target_ip_ctrl:target IP is %s.', target_ip)
ret = self._call_method('GetSystemNetCfg')
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('_get_target_ip_ctrl:get iscsi port list fail. '
'with Return code: %(ret)s.') %
{'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
count = ret['data']['sdwDeviceNum']
for index in range(0, count):
systemnetcfg = ret['data']['tSystemNetCfg'][index]
if target_ip == systemnetcfg['cIpAddr']:
return systemnetcfg['udwCtrlId']
return None
def _get_tgt_iqn(self, iscsiip):
# as the given iscsiip,we need to find it's ctrl number
ip_ctrl = self._get_target_ip_ctrl(iscsiip)
if ip_ctrl is None:
LOG.exception('_get_tgt_iqn:get iscsi ip ctrl fail, '
'IP is %s.', iscsiip)
return None
# get the ctrl iqn
ret = self._call_method('GetIscsiTargetName')
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
return None
target_info = ret['data']['tIscsiTargetInfo']
ctrl_count = ret['data']['udwCtrlCount']
for index in range(0, ctrl_count):
if ip_ctrl == target_info[index]['udwCtrlId']:
return target_info[index]['cTgtName']
return None
def _create_group(self, initiator_name, map_group_name):
map_grp_info = {'cMapGrpName': map_group_name}
ret = self._call_method('CreateMapGrp', map_grp_info)
if ((ret['returncode'] == zte_pub.ZTE_SUCCESS) or
(ret['returncode'] == zte_pub.ZTE_ERR_GROUP_EXIST)):
host_name = self._translate_host_name(initiator_name)
host_info = {'cHostAlias': host_name, 'ucOs': 1, 'ucType': 1,
'cPortName': initiator_name,
'sdwMultiPathMode': 1, 'cMulChapPass': ''}
# create host
ret = self._call_method('CreateHost', host_info)
if ret['returncode'] not in [zte_pub.ZTE_SUCCESS,
zte_pub.ZTE_ERR_HOSTNAME_EXIST,
zte_pub.ZTE_ERR_PORT_EXIST,
zte_pub.ZTE_ERR_PORT_EXIST_OLD]:
err_msg = (
_('create host failed. Host name:%(name)s '
'with Return code: %(ret)s.') %
{'name': host_name, 'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
# If port deleted by user, add it.
port_info = {
'cHostAlias': host_name,
'ucType': 1,
'cPortName': initiator_name,
'sdwMultiPathMode': 1,
'cMulChapPass': ''}
ret = self._call_method('AddPortToHost', port_info)
if ret['returncode'] not in [zte_pub.ZTE_SUCCESS,
zte_pub.ZTE_ERR_PORT_EXIST,
zte_pub.ZTE_ERR_PORT_EXIST_OLD]:
err_msg = (_('_create_group:add port failed. Port name: '
'%(name)s with Return code: %(ret)s.') %
{'name': initiator_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
host_in_grp = {
'ucInitName': host_name,
'cMapGrpName': map_group_name}
ret = self._call_method('AddHostToGrp', host_in_grp)
if ret['returncode'] not in [zte_pub.ZTE_SUCCESS,
zte_pub.ZTE_ERR_HOST_EXIST,
zte_pub.ZTE_ERR_HOST_EXIST_OLD]:
self._delete_group(map_group_name)
err_msg = (_('_create_group:add host to group failed. '
'group name:%(name)s init name :%(init)s '
'with Return code: %(ret)s.') %
{'name': map_group_name,
'init': host_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
else:
err_msg = (_('create group failed. Group name:%(name)s '
'with Return code: %(ret)s.') %
{'name': map_group_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
def _map_delete_lun(self, lunid, initiator_name):
map_group_name = self._translate_grp_name(initiator_name)
# lun not exist, no need to delete
if (lunid != zte_pub.ZTE_LUNID_NULL
and lunid is not None):
del_vol_from_grp = {
'cMapGrpName': map_group_name,
'sdwLunId': lunid}
ret = self._call_method('DelVolFromGrp', del_vol_from_grp)
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('_map_lun:delete lunid from group failed. '
'group name:%(name)s lunid : %(lun)s '
'with Return code: %(ret)s.') %
{'name': map_group_name, 'lun': lunid,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
# if no lun in group,then we will delete this group
lun_num = self._get_group_lunnum(map_group_name)
if lun_num == 0:
self._delete_group(map_group_name)
def _map_delete_host(self, map_group_name):
map_grp_info = {'cMapGrpName': map_group_name}
ret = self._call_method('GetMapGrpInfo', map_grp_info)
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('_map_delete_host:get map group info failed. '
'group name:%(name)s with Return code: %(ret)s.') %
{'name': map_group_name, 'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
sdwhostnum = ret['data']['sdwHostNum']
if sdwhostnum > 0:
thostinfo = ret['data']['tHostInfo']
for hostindex in range(0, int(sdwhostnum)):
initiator_name = thostinfo[hostindex]['ucHostName']
host_in_grp = {
'ucInitName': initiator_name,
'cMapGrpName': map_group_name}
ret = self._call_method('DelHostFromGrp', host_in_grp)
if ret['returncode'] == zte_pub.ZTE_ERR_GROUP_NOT_EXIST:
continue
if ret['returncode'] not in [zte_pub.ZTE_SUCCESS,
zte_pub.ZTE_ERR_HOST_NOT_EXIST]:
msg = _('delete host from group failed. ')
raise exception.VolumeDriverException(message=msg)
ret = self._call_method(
'GetHost', {"cHostAlias": initiator_name})
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('_map_delete_host:get host info failed. '
'host name:%(name)s with Return code: '
'%(ret)s.') %
{'name': initiator_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
return_data = ret['data']
portnum = return_data['sdwPortNum']
for portindex in range(0, int(portnum)):
port_host_info = {}
port_info = return_data['tPort']
port_name = port_info[portindex]['cPortName']
port_host_info['cPortName'] = port_name
port_host_info['cHostAlias'] = initiator_name
ret = self._call_method('DelPortFromHost', port_host_info)
if ret['returncode'] != zte_pub.ZTE_SUCCESS:
err_msg = (_('delete port from host failed. '
'host name:%(name)s, port name:%(port)s '
'with Return code: %(ret)s.') %
{'name': initiator_name,
'port': port_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)
ret = self._call_method(
'DelHost', {"cHostAlias": initiator_name})
if (ret['returncode'] not
in [zte_pub.ZTE_SUCCESS,
zte_pub.ZTE_ERR_HOSTNAME_NOT_EXIST]):
err_msg = (_('_map_delete_host: delete host failed. '
'host name:%(name)s with Return code: '
'%(ret)s') %
{'name': initiator_name,
'ret': ret['returncode']})
raise exception.VolumeBackendAPIException(data=err_msg)