deb-cinder/cinder/volume/drivers/zte/zte_ks.py
Walter A. Boring IV 1a5de5d4bd CI: Add CI_WIKI_NAME to all drivers
This patch adds a CI_WIKI_NAME to each driver object.  The value is the exact
name of the ThirdPartySystems wiki page.   This allows us to create an
automated tool to associated jobs to drivers and track their CI reporting
status correctly.

This patch also updates the generate_driver_list.py script to output the
driver list as a python list of dicts that can be directly consumed.

Change-Id: I0ec5f705e91f680a731648cf50738ea219565f70
2016-08-09 08:24:00 -07:00

984 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 _, _LE, _LI
from cinder import interface
from cinder import utils
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.IntOpt('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)
@interface.volumedriver
class ZTEVolumeDriver(driver.VolumeDriver):
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'
response = urllib.request.urlopen(req,
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(_LE('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(_LI('heartbeat failed. Return code:'
' %(ret)s.'),
{'ret': ret['returncode']})
except Exception:
LOG.exception(_LE('_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 a 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': True,
'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."""
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(_LE('_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(_LE('_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)