Add ZTE Block Storage Driver
It will support the minimum set of features required in Newton: * Volume Create/Delete * Volume Attach/Detach * Snapshot Create/Delete * Create Volume from Snapshot * Copy Image to Volume * Copy Volume to Image * Clone Volume * Extend Volume DocImpact Implements: bp zte-cinder-driver Change-Id: I1629ef022a5227a7c488909152f78f33719bc573
This commit is contained in:
parent
025213a6b1
commit
c8dc22af4b
@ -164,6 +164,7 @@ from cinder.volume.drivers.zfssa import zfssaiscsi as \
|
||||
cinder_volume_drivers_zfssa_zfssaiscsi
|
||||
from cinder.volume.drivers.zfssa import zfssanfs as \
|
||||
cinder_volume_drivers_zfssa_zfssanfs
|
||||
from cinder.volume.drivers.zte import zte_ks as cinder_volume_drivers_zte_zteks
|
||||
from cinder.volume import manager as cinder_volume_manager
|
||||
from cinder.wsgi import eventlet_server as cinder_wsgi_eventletserver
|
||||
from cinder.zonemanager.drivers.brocade import brcd_fabric_opts as \
|
||||
@ -221,6 +222,7 @@ def list_opts():
|
||||
cinder_volume_drivers_fujitsu_eternusdxcommon.
|
||||
FJ_ETERNUS_DX_OPT_opts,
|
||||
cinder_volume_drivers_ibm_gpfs.gpfs_opts,
|
||||
cinder_volume_drivers_zte_zteks.zte_opts,
|
||||
cinder_volume_drivers_violin_v7000common.violin_opts,
|
||||
cinder_volume_drivers_nexenta_options.NEXENTA_CONNECTION_OPTS,
|
||||
cinder_volume_drivers_nexenta_options.NEXENTA_ISCSI_OPTS,
|
||||
|
427
cinder/tests/unit/test_zte_ks.py
Normal file
427
cinder/tests/unit/test_zte_ks.py
Normal file
@ -0,0 +1,427 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""
|
||||
Self test for ZTE Storage Driver platform.
|
||||
"""
|
||||
from oslo_config import cfg
|
||||
|
||||
from cinder import exception
|
||||
from cinder import test
|
||||
from cinder.volume import configuration as conf
|
||||
from cinder.volume.drivers.zte import zte_ks
|
||||
from cinder.volume.drivers.zte import zte_pub
|
||||
|
||||
|
||||
session_id = 'kfomqdnoetjcjlva'
|
||||
volume_paras = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'size': 2,
|
||||
'volume_name': 'vol1',
|
||||
'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'vol1',
|
||||
'source_volid': None,
|
||||
'volume_metadata': [],
|
||||
'display_description': 'test volume',
|
||||
'volume_type_id': None}
|
||||
|
||||
volume_clone = {'name': 'volume-ee317512-f6a6-4284-a94e-5f4ac8783169',
|
||||
'size': 4,
|
||||
'volume_name': 'vol1',
|
||||
'id': 'ee317512-f6a6-4284-a94e-5f4ac8783169',
|
||||
'volume_id': 'ee317512-f6a6-4284-a94e-5f4ac8783169',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'clone_vol1',
|
||||
'source_volid': None,
|
||||
'volume_metadata': [],
|
||||
'display_description': 'test clone volume',
|
||||
'volume_type_id': None}
|
||||
snapvolume_paras = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'size': 2,
|
||||
'volume_size': 2,
|
||||
'volume_name': 'vol1',
|
||||
'id': '21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'volume_id': '21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'vol1',
|
||||
'source_volid': None,
|
||||
'volume_metadata': [],
|
||||
'display_description': 'test volume',
|
||||
'volume_type_id': None}
|
||||
connector = {'ip': '10.0.0.0',
|
||||
'initiator': 'iqn.1993-08.org.debian:01:222'}
|
||||
fcconnector = {'ip': '10.0.0.0',
|
||||
'wwpns': [1, 2, 3, 4, 5, 6, 7, 8]}
|
||||
fake_opt = [
|
||||
cfg.StrOpt('fake_opt', default='fake', help='fake opts')
|
||||
]
|
||||
VolFlowLimitAttr_paras = {'sqwWriteFlowLimit': 0,
|
||||
'cVolName': 'OpenCos_5072124445952515861',
|
||||
'sqwTotalFlowLimit': 500,
|
||||
'sqwWriteIoCount': 0,
|
||||
'sqwTotalIoCount': 0,
|
||||
'sqwReadFlowLimit': 0,
|
||||
'sqwReadIoCount': 0}
|
||||
|
||||
volume_name = 'OpenCos_8359669312515962256'
|
||||
return_success = {'returncode': zte_pub.ZTE_SUCCESS, 'data': {}}
|
||||
return_error = {'returncode': zte_pub.ZTE_ERR_LUNDEV_NOT_EXIST, 'data': {}}
|
||||
return_port_error = (
|
||||
{'returncode': zte_pub.ZTE_ERR_PORT_EXIST_INOTHER, 'data': {}})
|
||||
return_host_error = (
|
||||
{'returncode': zte_pub.ZTE_ERR_HOST_EXIST_INOTHER, 'data': {}})
|
||||
|
||||
MAP_TO_RESPONSE = {}
|
||||
signin_info = {'sessionID': session_id}
|
||||
MAP_TO_RESPONSE['plat.session.signin'] = {'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': signin_info}
|
||||
MAP_TO_RESPONSE['plat.session.heartbeat'] = return_success
|
||||
pool_info = {'sdwState': 1, 'qwTotalCapacity': 1024560,
|
||||
'qwFreeCapacity': 102456}
|
||||
MAP_TO_RESPONSE['GetPoolInfo'] = {'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': pool_info}
|
||||
MAP_TO_RESPONSE['CreateVolOnPool'] = return_success
|
||||
MAP_TO_RESPONSE['DelCvol'] = return_success
|
||||
MAP_TO_RESPONSE['GetCvolNamesOnVol'] = {
|
||||
'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': {'sdwCvolNum': 2, 'scCvolNames': [{'scCvolName': 'clone1'},
|
||||
{'scCvolName': 'clone2'}]}}
|
||||
MAP_TO_RESPONSE['CreateSvol'] = return_success
|
||||
MAP_TO_RESPONSE['DelSvol'] = return_success
|
||||
MAP_TO_RESPONSE['ExpandVolOnPool'] = return_success
|
||||
MAP_TO_RESPONSE['CreateCvol'] = return_success
|
||||
cVolName_from_vol_name = "OpenCos_9fbc232bf71ee2fa8bd"
|
||||
grp_info = {'sdwHostNum': 1,
|
||||
'tHostInfo': [{'ucHostName': 'host1'}],
|
||||
'sdwLunNum': 5,
|
||||
'cMapGrpName': 'group_cjf',
|
||||
'tLunInfo': [{'sdwLunState': 0, 'sdwBlockSize': 0,
|
||||
'sdwAccessAttr': 0, 'sdwLunId': 0,
|
||||
'cVolName': 'vol1'},
|
||||
{'sdwLunState': 0, 'sdwBlockSize': 0,
|
||||
'sdwAccessAttr': 0, 'sdwLunId': 1,
|
||||
'cVolName': volume_name},
|
||||
{'sdwLunState': 0, 'sdwBlockSize': 0,
|
||||
'sdwAccessAttr': 0, 'sdwLunId': 2,
|
||||
'cVolName': 'vol3'},
|
||||
{'sdwLunState': 0, 'sdwBlockSize': 0,
|
||||
'sdwAccessAttr': 0, 'sdwLunId': 3,
|
||||
'cVolName': cVolName_from_vol_name},
|
||||
{'sdwLunState': 0, 'sdwBlockSize': 0,
|
||||
'sdwAccessAttr': 0, 'sdwLunId': 5,
|
||||
'cVolName': 'vol4'}]}
|
||||
MAP_TO_RESPONSE['GetMapGrpInfo'] = (
|
||||
{'returncode': zte_pub.ZTE_SUCCESS, 'data': grp_info})
|
||||
MAP_TO_RESPONSE['DelMapGrp'] = return_success
|
||||
simple_grp_info = {'sdwMapGrpNum': 0,
|
||||
'tMapGrpSimpleInfo': [{'sdwHostNum': 0,
|
||||
'sdwLunNum': 0,
|
||||
'cMapGrpName': session_id},
|
||||
{'sdwHostNum': 1,
|
||||
'sdwLunNum': 1,
|
||||
'cMapGrpName': ''}]}
|
||||
MAP_TO_RESPONSE['GetGrpSimpleInfoList'] = {'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': simple_grp_info}
|
||||
luninfo = {'sdwLunId': 3}
|
||||
MAP_TO_RESPONSE['AddVolToGrp'] = {'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': luninfo}
|
||||
sys_info = {'cVendor': 'ZTE', 'cVersionName': 'V1.0',
|
||||
'storage_protocol': 'iSCSI'}
|
||||
MAP_TO_RESPONSE['GetSysInfo'] = {'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': sys_info}
|
||||
cfg_info = {'sdwDeviceNum': 4,
|
||||
'tSystemNetCfg': [
|
||||
{'udwCtrlId': 0, 'udwRoleType': 0,
|
||||
'udwPortType': 1, 'udwDeviceId': 123,
|
||||
'cIpAddr': '198.51.100.20'},
|
||||
{'udwCtrlId': 0, 'udwRoleType': 0,
|
||||
'udwPortType': 1, 'udwDeviceId': 123,
|
||||
'cIpAddr': '198.51.100.21'},
|
||||
{'udwCtrlId': 0, 'udwRoleType': 0,
|
||||
'udwPortType': 1, 'udwDeviceId': 123,
|
||||
'cIpAddr': '198.51.100.22'},
|
||||
{'udwCtrlId': 0, 'udwRoleType': 0,
|
||||
'udwPortType': 1, 'udwDeviceId': 123,
|
||||
'cIpAddr': '198.51.100.23'}]}
|
||||
MAP_TO_RESPONSE['GetSystemNetCfg'] = {'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': cfg_info}
|
||||
iscsi_target = {
|
||||
'tIscsiTargetInfo': [
|
||||
{'udwCtrlId': 0,
|
||||
'cTgtName': 'iqn.2099-01.cn.com.zte:usp.spr11-00:00:22:15'},
|
||||
{'udwCtrlId': 0,
|
||||
'cTgtName': 'iqn.2099-01.cn.com.zte:usp.spr11-00:00:22:25'}],
|
||||
'udwCtrlCount': 2}
|
||||
MAP_TO_RESPONSE['GetIscsiTargetName'] = {'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': iscsi_target}
|
||||
MAP_TO_RESPONSE['CreateMapGrp'] = return_success
|
||||
grp_info_forsearch = {'cVolName': 'vol1',
|
||||
'sdwMapGrpNum': 1,
|
||||
'cMapGrpNames': ['grp1'],
|
||||
'sdwLunLocalId': [1]}
|
||||
MAP_TO_RESPONSE['GetGrpNamesOfVol'] = (
|
||||
{'returncode': zte_pub.ZTE_SUCCESS, 'data': grp_info_forsearch})
|
||||
MAP_TO_RESPONSE['CreateHost'] = return_success
|
||||
MAP_TO_RESPONSE['AddPortToHost'] = return_success
|
||||
MAP_TO_RESPONSE['AddHostToGrp'] = return_success
|
||||
MAP_TO_RESPONSE['DelVolFromGrp'] = return_success
|
||||
MAP_TO_RESPONSE['DelHostFromGrp'] = return_success
|
||||
host_info = {'sdwPortNum': 2,
|
||||
'tPort': [{'cPortName': 'port1'},
|
||||
{'cPortName': 'port2'}]}
|
||||
MAP_TO_RESPONSE['GetHost'] = {'returncode': zte_pub.ZTE_SUCCESS,
|
||||
'data': host_info}
|
||||
MAP_TO_RESPONSE['DelPortFromHost'] = return_success
|
||||
MAP_TO_RESPONSE['DelHost'] = return_success
|
||||
|
||||
|
||||
class FakeZteISCSIDriver(zte_ks.ZteISCSIDriver):
|
||||
def __init__(self, configuration):
|
||||
self.configuration = configuration
|
||||
super(FakeZteISCSIDriver, self).__init__(
|
||||
configuration=self.configuration)
|
||||
self.result = zte_pub.ZTE_SUCCESS
|
||||
self.test_flag = True
|
||||
self.portexist_flag = False
|
||||
self.portexistother_flag = False
|
||||
self.hostexist_flag = False
|
||||
self.hostexistother_flag = False
|
||||
|
||||
def _call(self, sessionid='', method='', params=None):
|
||||
return_data = return_success
|
||||
|
||||
if method in MAP_TO_RESPONSE.keys():
|
||||
return_data = MAP_TO_RESPONSE[method]
|
||||
|
||||
if not self.test_flag:
|
||||
return_data = return_error
|
||||
if self.portexistother_flag:
|
||||
return_data = return_port_error
|
||||
if self.hostexistother_flag:
|
||||
return_data = return_host_error
|
||||
return return_data
|
||||
|
||||
def _check_conf_file(self):
|
||||
pass
|
||||
|
||||
def _get_iscsi_info(self):
|
||||
iscsi_info = {'DefaultTargetIPs': ["198.51.100.20"]}
|
||||
|
||||
return iscsi_info
|
||||
|
||||
|
||||
class ZteBaseDriverTestCase(object):
|
||||
def test_create_volume_success(self):
|
||||
self.driver.test_flag = True
|
||||
self.driver.create_volume(volume_paras)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_create_volume_fail(self):
|
||||
self.driver.test_flag = False
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.create_volume, volume_paras)
|
||||
|
||||
def test_delete_volume_success(self):
|
||||
self.driver.test_flag = True
|
||||
self.driver.delete_volume(volume_paras)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_delete_volume_fail(self):
|
||||
self.driver.test_flag = False
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.delete_volume, volume_paras)
|
||||
|
||||
def test_delete_cloned_volume_success(self):
|
||||
self.driver.test_flag = True
|
||||
vol = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'source_volid': '68a52c1e-ecbe-4f6c-954c-9f551347ff3f'}
|
||||
self.driver.delete_volume(vol)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_delete_cloned_volume_fail(self):
|
||||
self.driver.test_flag = False
|
||||
vol = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'source_volid': '68a52c1e-ecbe-4f6c-954c-9f551347ff3f'}
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.delete_volume, vol)
|
||||
|
||||
def test_create_snapshot_success(self):
|
||||
self.driver.test_flag = True
|
||||
snap_vol = {'name': 'snapshot-2b9b982a-8b56-46e3-9d4f-6392e8a72e6e',
|
||||
'volume_name':
|
||||
'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'volume_size': 2}
|
||||
self.driver.create_snapshot(snap_vol)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_create_snapshot_fail(self):
|
||||
self.driver.test_flag = False
|
||||
snap_vol = {'name': 'snapshot-2b9b982a-8b56-46e3-9d4f-6392e8a72e6e',
|
||||
'volume_name':
|
||||
'volume-21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
'volume_size': 2}
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.create_snapshot, snap_vol)
|
||||
|
||||
def test_delete_snapshot_success(self):
|
||||
self.driver.test_flag = True
|
||||
snap_vol = {'name': 'snapshot-2b9b982a-8b56-46e3-9d4f-6392e8a72e6e'}
|
||||
self.driver.delete_snapshot(snap_vol)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_delete_snapshot_fail(self):
|
||||
self.driver.test_flag = False
|
||||
snap_vol = {'name': 'snapshot-2b9b982a-8b56-46e3-9d4f-6392e8a72e6e'}
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.delete_snapshot, snap_vol)
|
||||
|
||||
def test_extend_volume_success(self):
|
||||
self.driver.test_flag = True
|
||||
self.driver.extend_volume(volume_paras, 4)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_extend_volume_fail(self):
|
||||
self.driver.test_flag = False
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.extend_volume, volume_paras, 4)
|
||||
|
||||
def test_create_cloned_volume_success(self):
|
||||
self.driver.test_flag = True
|
||||
self.driver.create_cloned_volume(volume_clone, volume_paras)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_create_cloned_volume_fail(self):
|
||||
self.driver.test_flag = False
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.create_cloned_volume,
|
||||
volume_clone, volume_paras)
|
||||
|
||||
def test_create_volume_from_snapshot_success(self):
|
||||
self.driver.test_flag = True
|
||||
self.driver.create_volume_from_snapshot(volume_clone, snapvolume_paras)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_create_volume_from_snapshot_fail(self):
|
||||
self.driver.test_flag = False
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.create_cloned_volume,
|
||||
volume_clone, volume_paras)
|
||||
|
||||
|
||||
class ZteISCSIDriverTestCase(ZteBaseDriverTestCase, test.TestCase):
|
||||
"""Test ZTE iSCSI volume driver."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ZteISCSIDriverTestCase, self).__init__(*args, **kwargs)
|
||||
|
||||
def setUp(self):
|
||||
super(ZteISCSIDriverTestCase, self).setUp()
|
||||
configuration = conf.Configuration(None)
|
||||
self.configuration = configuration
|
||||
|
||||
self.configuration.zteControllerIP0 = '192.0.2.2'
|
||||
self.configuration.zteLocalIP = '192.0.2.8'
|
||||
self.configuration.zteUserName = 'root'
|
||||
self.configuration.zteUserPassword = 'root'
|
||||
self.configuration.zteChunkSize = 64
|
||||
self.configuration.zteAheadReadSize = 8
|
||||
self.configuration.zteCachePolicy = 65535
|
||||
self.configuration.zteSSDCacheSwitch = 0
|
||||
self.configuration.zteStoragePool = 'pool1,pool2,pool3'
|
||||
self.configuration.ztePoolVolAllocPolicy = 0
|
||||
self.configuration.ztePoolVolMovePolicy = 0
|
||||
self.configuration.ztePoolVolIsThin = 0
|
||||
self.configuration.ztePoolVolInitAllocedCapacity = 0
|
||||
self.configuration.ztePoolVolAlarmThreshold = 0
|
||||
self.configuration.ztePoolVolAlarmStopAllocFlag = 0
|
||||
|
||||
self.driver = FakeZteISCSIDriver(configuration=self.configuration)
|
||||
self.driver.do_setup({})
|
||||
|
||||
def tearDown(self):
|
||||
super(ZteISCSIDriverTestCase, self).tearDown()
|
||||
|
||||
def test_get_volume_stats(self):
|
||||
stats = self.driver.get_volume_stats(True)
|
||||
self.assertEqual("ZTE", stats["vendor_name"])
|
||||
self.assertEqual("iSCSI", stats["storage_protocol"])
|
||||
self.assertEqual("V1.0", stats["driver_version"])
|
||||
self.assertLess(0, stats["total_capacity_gb"])
|
||||
|
||||
def test_initialize_connection_success(self):
|
||||
self.driver.test_flag = True
|
||||
data = self.driver.initialize_connection(volume_paras, connector)
|
||||
properties = data['data']
|
||||
self.assertEqual("iscsi", data["driver_volume_type"])
|
||||
self.assertEqual('iqn.2099-01.cn.com.zte:usp.spr11-00:00:22:15',
|
||||
properties["target_iqn"])
|
||||
self.assertEqual(3, properties["target_lun"])
|
||||
self.assertEqual('198.51.100.20:3260', properties["target_portal"])
|
||||
|
||||
def test_initialize_connection_portexist(self):
|
||||
self.driver.portexist_flag = True
|
||||
data = self.driver.initialize_connection(volume_paras, connector)
|
||||
properties = data['data']
|
||||
self.assertEqual("iscsi", data["driver_volume_type"])
|
||||
self.assertEqual('iqn.2099-01.cn.com.zte:usp.spr11-00:00:22:15',
|
||||
properties["target_iqn"])
|
||||
self.assertEqual(3, properties["target_lun"])
|
||||
self.assertEqual('198.51.100.20:3260', properties["target_portal"])
|
||||
|
||||
def test_initialize_connection_hostexist(self):
|
||||
self.driver.hostexist_flag = True
|
||||
data = self.driver.initialize_connection(volume_paras, connector)
|
||||
|
||||
properties = data['data']
|
||||
self.assertEqual("iscsi", data["driver_volume_type"])
|
||||
self.assertEqual('iqn.2099-01.cn.com.zte:usp.spr11-00:00:22:15',
|
||||
properties["target_iqn"])
|
||||
self.assertEqual(3, properties["target_lun"])
|
||||
self.assertEqual('198.51.100.20:3260', properties["target_portal"])
|
||||
|
||||
def test_initialize_connection_portexistother(self):
|
||||
self.driver.portexistother_flag = True
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.initialize_connection,
|
||||
volume_paras, connector)
|
||||
|
||||
def test_initialize_connection_hostexistother(self):
|
||||
self.driver.hostexistother_flag = True
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.initialize_connection,
|
||||
volume_paras, connector)
|
||||
|
||||
def test_initialize_connection_fail(self):
|
||||
self.driver.test_flag = False
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.initialize_connection,
|
||||
volume_paras, connector)
|
||||
|
||||
def test_terminate_connection_success(self):
|
||||
self.driver.test_flag = True
|
||||
vol = {'name': 'volume-ee317512-f6a6-4284-a94e-5f4ac8783169'}
|
||||
self.driver.terminate_connection(vol, connector)
|
||||
self.assertEqual(zte_pub.ZTE_SUCCESS, self.driver.result)
|
||||
|
||||
def test_terminate_connection_fail(self):
|
||||
self.driver.test_flag = False
|
||||
vol = {'name': 'volume-ee317512-f6a6-4284-a94e-5f4ac8783169'}
|
||||
self.assertRaises(exception.CinderException,
|
||||
self.driver.terminate_connection, vol, connector)
|
0
cinder/volume/drivers/zte/__init__.py
Normal file
0
cinder/volume/drivers/zte/__init__.py
Normal file
982
cinder/volume/drivers/zte/zte_ks.py
Normal file
982
cinder/volume/drivers/zte/zte_ks.py
Normal file
@ -0,0 +1,982 @@
|
||||
# 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 + "¶ms=" + 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)
|
59
cinder/volume/drivers/zte/zte_pub.py
Normal file
59
cinder/volume/drivers/zte/zte_pub.py
Normal file
@ -0,0 +1,59 @@
|
||||
# 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.
|
||||
"""
|
||||
Constants used in ZTE Volume driver.
|
||||
"""
|
||||
|
||||
ZTE_HOST_GROUP_NAME_PREFIX = 'HostGroup_'
|
||||
ZTE_VOL_AND_SNAP_NAME_PREFIX = 'OpenStack_'
|
||||
ZTE_VOL_NAME_PREFIX_NEW = 'OpenCos_'
|
||||
ZTE_CLONE_SUFFIX = 'c'
|
||||
ZTE_GROUP_MAX_LUN = 255
|
||||
ZTE_SUCCESS = 0
|
||||
ZTE_VOLUME_IN_GROUP = 16917029
|
||||
ZTE_WEB_LOGIN_TYPE = 5
|
||||
ZTE_ERR_GROUP_NOT_EXIST = 16916997
|
||||
ZTE_ERR_GROUP_EXIST = 16916994
|
||||
ZTE_ERR_HOSTNAME_EXIST = 16918785
|
||||
ZTE_ERR_HOSTNAME_NOT_EXIST = 16918786
|
||||
ZTE_ERR_PORT_EXIST_OLD = 16918787
|
||||
ZTE_ERR_PORT_EXIST = 16918798
|
||||
ZTE_ERR_PORT_EXIST_INOTHER = 16918799
|
||||
ZTE_ERR_HOST_NOT_EXIST = 16917004
|
||||
ZTE_ERR_HOST_EXIST_OLD = 16917002
|
||||
ZTE_ERR_HOST_EXIST = 16917015
|
||||
ZTE_ERR_HOST_EXIST_INOTHER = 16917016
|
||||
ZTE_ERR_VOLUME_NOT_EXIST = 17108999
|
||||
ZTE_ERR_OBJECT_EXIST = 16917159
|
||||
ZTE_ERR_CLONE_OR_SNAP_NOT_EXIST = 16917040
|
||||
ZTE_ERR_VOL_EXISTS = 16917000
|
||||
ZTE_ERR_LUNDEV_NOT_EXIST = 16973826
|
||||
ZTE_ERR_VAS_OBJECT_NOT_EXIST = 17436681
|
||||
ZTE_VOLUME_TASK_NOT_FINISHED = 17436959
|
||||
ZTE_ERR_SNAP_EXIST_CLONE = 16917163
|
||||
ZTE_SESSION_EXIST = 1495
|
||||
ZTE_STATUS_OK = 1
|
||||
|
||||
ZTE_VOLUME = 0
|
||||
ZTE_SNAPSHOT = 1
|
||||
|
||||
ZTE_LUNID_NULL = -1
|
||||
|
||||
ZTE_VOLUME_ALLOCATION_RATIO = 20
|
||||
ZTE_VOLUME_SNAPSHOT_PERCENT = 50
|
||||
ZTE_DEFAULT_TIMEOUT = 720
|
||||
|
||||
ZTE_SNAPSHOT_MODE_READ_ONLY = 0
|
||||
ZTE_SNAPSHOT_MODE_RW = 1
|
4
releasenotes/notes/zte_cinder_driver-76ba6d034e1b6f65.yaml
Executable file
4
releasenotes/notes/zte_cinder_driver-76ba6d034e1b6f65.yaml
Executable file
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Added backend driver for ZTE iSCSI storage.
|
||||
|
Loading…
Reference in New Issue
Block a user