Add support for enhanced features to the QNAP Cinder driver

This adds enhanced supports to the QNAP Cinder driver:
 - CHAP
 - Thin Provision
 - SSD Cache
 - Dedupe
 - Compression

DocImpact
Implements: blueprint qnap-enhance-support

Change-Id: I2a7440789753bb0e42ac0e8d0190b21652a87e2f
This commit is contained in:
Pony Chou 2017-06-02 15:49:21 +08:00 committed by Chris Yang
parent 92d9f19f99
commit 08dcf03541
3 changed files with 256 additions and 135 deletions

View File

@ -22,6 +22,7 @@ except ImportError:
from ddt import data from ddt import data
from ddt import ddt from ddt import ddt
from ddt import unpack from ddt import unpack
import eventlet
import mock import mock
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import units from oslo_utils import units
@ -594,19 +595,26 @@ def create_configuration(
management_url, management_url,
san_iscsi_ip, san_iscsi_ip,
poolname, poolname,
thin_provision=True): thin_provision=True,
compression=True,
deduplication=False,
ssd_cache=False):
"""Create configuration.""" """Create configuration."""
configuration = mock.Mock() configuration = mock.Mock()
configuration.san_login = username configuration.san_login = username
configuration.san_password = password configuration.san_password = password
configuration.qnap_management_url = management_url configuration.qnap_management_url = management_url
configuration.san_thin_provision = thin_provision configuration.san_thin_provision = thin_provision
configuration.qnap_compression = compression
configuration.qnap_deduplication = deduplication
configuration.qnap_ssd_cache = ssd_cache
configuration.san_iscsi_ip = san_iscsi_ip configuration.san_iscsi_ip = san_iscsi_ip
configuration.qnap_poolname = poolname configuration.qnap_poolname = poolname
configuration.safe_get.return_value = 'QNAP' configuration.safe_get.return_value = 'QNAP'
configuration.iscsi_ip_address = '1.2.3.4' configuration.iscsi_ip_address = '1.2.3.4'
configuration.qnap_storage_protocol = 'iscsi' configuration.qnap_storage_protocol = 'iscsi'
configuration.reserved_percentage = 0 configuration.reserved_percentage = 0
configuration.use_chap_auth = False
return configuration return configuration
@ -679,6 +687,14 @@ class VolumeClass(object):
'name': 'fakeTargetIqn', 'name': 'fakeTargetIqn',
'tgt_lun': '1' 'tgt_lun': '1'
} }
self.volume_type = {
'extra_specs': {
'qnap_thin_provision': 'True',
'qnap_compression': 'True',
'qnap_deduplication': 'False',
'qnap_ssd_cache': 'False'
}
}
def __getitem__(self, arg): def __getitem__(self, arg):
"""Getitem.""" """Getitem."""
@ -689,7 +705,8 @@ class VolumeClass(object):
'name': self.name, 'name': self.name,
'volume_metadata': self.volume_metadata, 'volume_metadata': self.volume_metadata,
'metadata': self.metadata, 'metadata': self.metadata,
'provider_location': self.provider_location 'provider_location': self.provider_location,
'volume_type': self.volume_type
}[arg] }[arg]
def __contains__(self, arg): def __contains__(self, arg):
@ -701,7 +718,8 @@ class VolumeClass(object):
'name': self.name, 'name': self.name,
'volume_metadata': self.volume_metadata, 'volume_metadata': self.volume_metadata,
'metadata': self.metadata, 'metadata': self.metadata,
'provider_location': self.provider_location 'provider_location': self.provider_location,
'volume_type': self.volume_type
}[arg] }[arg]
def __setitem__(self, key, value): def __setitem__(self, key, value):
@ -1288,6 +1306,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Pool1', 'Pool1',
True)) True))
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
self.driver.create_volume(fake_volume) self.driver.create_volume(fake_volume)
@ -1295,7 +1314,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
fake_volume, fake_volume,
self.driver.configuration.qnap_poolname, self.driver.configuration.qnap_poolname,
'fakeLun', 'fakeLun',
True) True, False, True, False)
expected_call_list = [ expected_call_list = [
mock.call(LUNName='fakeLun'), mock.call(LUNName='fakeLun'),
@ -1442,6 +1461,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'http://1.2.3.4:8080', 'http://1.2.3.4:8080',
'Pool1', 'Pool1',
True)) True))
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
self.driver.create_cloned_volume(fake_volume, fake_src_vref) self.driver.create_cloned_volume(fake_volume, fake_src_vref)
@ -1506,19 +1526,18 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Pool1', 'Pool1',
True)) True))
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
self.driver.create_cloned_volume(fake_volume, fake_src_vref) self.driver.create_cloned_volume(fake_volume, fake_src_vref)
mock_extend_lun.assert_called_once_with(fake_volume, 'fakeLunNaa') mock_extend_lun.assert_called_once_with(fake_volume, 'fakeLunNaa')
@mock.patch('eventlet.greenthread.sleep', return_value=None)
@mock.patch.object(qnap.QnapISCSIDriver, '_create_snapshot_name') @mock.patch.object(qnap.QnapISCSIDriver, '_create_snapshot_name')
@mock.patch('cinder.volume.drivers.qnap.QnapAPIExecutor') @mock.patch('cinder.volume.drivers.qnap.QnapAPIExecutor')
def test_create_snapshot_positive( def test_create_snapshot_positive(
self, self,
mock_api_executor, mock_api_executor,
mock_create_snapshot_name, mock_create_snapshot_name):
mock_greenthread_sleep):
"""Test create snapshot.""" """Test create snapshot."""
fake_volume = VolumeClass( fake_volume = VolumeClass(
'fakeDisplayName', 'fakeId', 100, 'fakeLunName') 'fakeDisplayName', 'fakeId', 100, 'fakeLunName')
@ -1542,6 +1561,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Pool1', 'Pool1',
True)) True))
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
self.driver.create_snapshot(snapshot) self.driver.create_snapshot(snapshot)
@ -1647,6 +1667,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Pool1', 'Pool1',
True)) True))
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
self.driver.create_volume_from_snapshot(fake_volume, fake_snapshot) self.driver.create_volume_from_snapshot(fake_volume, fake_snapshot)
@ -1743,7 +1764,11 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
free_capacity_gb=928732941681 / units.Gi, free_capacity_gb=928732941681 / units.Gi,
provisioned_capacity_gb=1480470528 / units.Gi, provisioned_capacity_gb=1480470528 / units.Gi,
reserved_percentage=self.driver.configuration.reserved_percentage, reserved_percentage=self.driver.configuration.reserved_percentage,
QoS_support=False) QoS_support=False,
qnap_thin_provision=['True', 'False'],
qnap_compression=['True', 'False'],
qnap_deduplication=['True', 'False'],
qnap_ssd_cache=['True', 'False'])
expected_res['pools'] = [single_pool] expected_res['pools'] = [single_pool]
self.assertEqual( self.assertEqual(
@ -1819,7 +1844,6 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'LUNStatus': '1'} 'LUNStatus': '1'}
mock_api_return.edit_lun.assert_called_once_with(expect_lun) mock_api_return.edit_lun.assert_called_once_with(expect_lun)
@mock.patch('eventlet.greenthread.sleep', return_value=None)
@mock.patch.object(qnap.QnapISCSIDriver, @mock.patch.object(qnap.QnapISCSIDriver,
'_get_lun_naa_from_volume_metadata') '_get_lun_naa_from_volume_metadata')
@mock.patch.object(qnap.QnapISCSIDriver, '_gen_random_name') @mock.patch.object(qnap.QnapISCSIDriver, '_gen_random_name')
@ -1828,8 +1852,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
self, self,
mock_api_executor, mock_api_executor,
mock_gen_random_name, mock_gen_random_name,
mock_get_lun_naa_from_volume_metadata, mock_get_lun_naa_from_volume_metadata):
mock_greenthread_sleep):
"""Test create export.""" """Test create export."""
fake_volume = VolumeClass( fake_volume = VolumeClass(
'fakeDisplayName', 'fakeId', 100, 'fakeLunName') 'fakeDisplayName', 'fakeId', 100, 'fakeLunName')
@ -1847,8 +1870,6 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
mock_api_return.create_target.return_value = 'fakeTargetIndex' mock_api_return.create_target.return_value = 'fakeTargetIndex'
mock_api_return.get_target_info.return_value = ( mock_api_return.get_target_info.return_value = (
self.get_target_info_return_value()) self.get_target_info_return_value())
mock_api_return.get_all_iscsi_portal_setting.return_value = (
FAKE_RES_DETAIL_GET_ALL_ISCSI_PORTAL_SETTING)
mock_api_return.map_lun.return_value = None mock_api_return.map_lun.return_value = None
mock_api_return.get_ethernet_ip.return_value = ['1.2.3.4'], None mock_api_return.get_ethernet_ip.return_value = ['1.2.3.4'], None
@ -1860,7 +1881,11 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Pool1', 'Pool1',
True)) True))
self.driver.configuration.use_chap_auth = False
self.driver.configuration.chap_username = ''
self.driver.configuration.chap_password = ''
self.driver.iscsi_port = 'fakeServicePort' self.driver.iscsi_port = 'fakeServicePort'
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
expected_properties = '%(host)s:%(port)s,1 %(name)s %(tgt_lun)s' % { expected_properties = '%(host)s:%(port)s,1 %(name)s %(tgt_lun)s' % {
@ -1874,7 +1899,6 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
self.assertEqual(expected_return, self.driver.create_export( self.assertEqual(expected_return, self.driver.create_export(
'context', fake_volume, fake_connector)) 'context', fake_volume, fake_connector))
@mock.patch('eventlet.greenthread.sleep', return_value=None)
@mock.patch.object(qnap.QnapISCSIDriver, @mock.patch.object(qnap.QnapISCSIDriver,
'_get_lun_naa_from_volume_metadata') '_get_lun_naa_from_volume_metadata')
@mock.patch.object(qnap.QnapISCSIDriver, '_gen_random_name') @mock.patch.object(qnap.QnapISCSIDriver, '_gen_random_name')
@ -1883,8 +1907,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
self, self,
mock_api_executor, mock_api_executor,
mock_gen_random_name, mock_gen_random_name,
mock_get_lun_naa_from_volume_metadata, mock_get_lun_naa_from_volume_metadata):
mock_greenthread_sleep):
"""Test create export.""" """Test create export."""
fake_volume = VolumeClass( fake_volume = VolumeClass(
'fakeDisplayName', 'fakeId', 100, 'fakeLunName') 'fakeDisplayName', 'fakeId', 100, 'fakeLunName')
@ -1902,8 +1925,6 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
mock_api_return.create_target.return_value = 'fakeTargetIndex' mock_api_return.create_target.return_value = 'fakeTargetIndex'
mock_api_return.get_target_info.return_value = ( mock_api_return.get_target_info.return_value = (
self.get_target_info_return_value()) self.get_target_info_return_value())
mock_api_return.get_all_iscsi_portal_setting.return_value = (
FAKE_RES_DETAIL_GET_ALL_ISCSI_PORTAL_SETTING)
mock_api_return.map_lun.return_value = None mock_api_return.map_lun.return_value = None
mock_api_return.get_ethernet_ip.return_value = ['1.2.3.4'], None mock_api_return.get_ethernet_ip.return_value = ['1.2.3.4'], None
@ -1915,7 +1936,11 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Pool1', 'Pool1',
True)) True))
self.driver.configuration.use_chap_auth = False
self.driver.configuration.chap_username = ''
self.driver.configuration.chap_password = ''
self.driver.iscsi_port = 'fakeServicePort' self.driver.iscsi_port = 'fakeServicePort'
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
expected_properties = '%(host)s:%(port)s,1 %(name)s %(tgt_lun)s' % { expected_properties = '%(host)s:%(port)s,1 %(name)s %(tgt_lun)s' % {
@ -1929,7 +1954,6 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
self.assertEqual(expected_return, self.driver.create_export( self.assertEqual(expected_return, self.driver.create_export(
'context', fake_volume, fake_connector)) 'context', fake_volume, fake_connector))
@mock.patch('eventlet.greenthread.sleep', return_value=None)
@mock.patch.object(qnap.QnapISCSIDriver, @mock.patch.object(qnap.QnapISCSIDriver,
'_get_lun_naa_from_volume_metadata') '_get_lun_naa_from_volume_metadata')
@mock.patch.object(qnap.QnapISCSIDriver, '_gen_random_name') @mock.patch.object(qnap.QnapISCSIDriver, '_gen_random_name')
@ -1938,8 +1962,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
self, self,
mock_api_executor, mock_api_executor,
mock_gen_random_name, mock_gen_random_name,
mock_get_lun_naa_from_volume_metadata, mock_get_lun_naa_from_volume_metadata):
mock_greenthread_sleep):
"""Test create export.""" """Test create export."""
fake_volume = VolumeClass( fake_volume = VolumeClass(
'fakeDisplayName', 'fakeId', 100, 'fakeLunName') 'fakeDisplayName', 'fakeId', 100, 'fakeLunName')
@ -1954,8 +1977,6 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
FAKE_RES_DETAIL_ISCSI_PORTAL_INFO) FAKE_RES_DETAIL_ISCSI_PORTAL_INFO)
mock_gen_random_name.return_value = 'fakeTargetName' mock_gen_random_name.return_value = 'fakeTargetName'
mock_get_lun_naa_from_volume_metadata.return_value = 'fakeLunNaa' mock_get_lun_naa_from_volume_metadata.return_value = 'fakeLunNaa'
mock_api_return.get_target_info_by_initiator.return_value = (
'fakeTargetIndex', 'fakeTargetIqn')
mock_api_return.create_target.return_value = 'fakeTargetIndex' mock_api_return.create_target.return_value = 'fakeTargetIndex'
mock_api_return.get_target_info.return_value = ( mock_api_return.get_target_info.return_value = (
self.get_target_info_return_value()) self.get_target_info_return_value())
@ -1971,7 +1992,11 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Pool1', 'Pool1',
True)) True))
self.driver.configuration.use_chap_auth = False
self.driver.configuration.chap_username = ''
self.driver.configuration.chap_password = ''
self.driver.iscsi_port = 'fakeServicePort' self.driver.iscsi_port = 'fakeServicePort'
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
expected_properties = '%(host)s:%(port)s,1 %(name)s %(tgt_lun)s' % { expected_properties = '%(host)s:%(port)s,1 %(name)s %(tgt_lun)s' % {
@ -1985,7 +2010,6 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
self.assertEqual(expected_return, self.driver.create_export( self.assertEqual(expected_return, self.driver.create_export(
'context', fake_volume, fake_connector)) 'context', fake_volume, fake_connector))
@mock.patch('eventlet.greenthread.sleep', return_value=None)
@mock.patch.object(qnap.QnapISCSIDriver, @mock.patch.object(qnap.QnapISCSIDriver,
'_get_lun_naa_from_volume_metadata') '_get_lun_naa_from_volume_metadata')
@mock.patch.object(qnap.QnapISCSIDriver, '_gen_random_name') @mock.patch.object(qnap.QnapISCSIDriver, '_gen_random_name')
@ -1996,8 +2020,7 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
mock_api_executor, mock_api_executor,
mock_api_executor_ts, mock_api_executor_ts,
mock_gen_random_name, mock_gen_random_name,
mock_get_lun_naa_from_volume_metadata, mock_get_lun_naa_from_volume_metadata):
mock_greenthread_sleep):
"""Test create export.""" """Test create export."""
fake_volume = VolumeClass( fake_volume = VolumeClass(
'fakeDisplayName', 'fakeId', 100, 'fakeLunName') 'fakeDisplayName', 'fakeId', 100, 'fakeLunName')
@ -2016,8 +2039,6 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
mock_api_return.create_target.return_value = 'fakeTargetIndex' mock_api_return.create_target.return_value = 'fakeTargetIndex'
mock_api_return.get_target_info.return_value = ( mock_api_return.get_target_info.return_value = (
self.get_target_info_return_value()) self.get_target_info_return_value())
mock_api_return.get_all_iscsi_portal_setting.return_value = (
FAKE_RES_DETAIL_GET_ALL_ISCSI_PORTAL_SETTING)
mock_api_return.map_lun.return_value = None mock_api_return.map_lun.return_value = None
mock_api_return.get_ethernet_ip.return_value = ['1.2.3.4'], None mock_api_return.get_ethernet_ip.return_value = ['1.2.3.4'], None
@ -2029,7 +2050,11 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Storage Pool 1', 'Storage Pool 1',
True)) True))
self.driver.configuration.use_chap_auth = False
self.driver.configuration.chap_username = ''
self.driver.configuration.chap_password = ''
self.driver.iscsi_port = 'fakeServicePort' self.driver.iscsi_port = 'fakeServicePort'
self.mock_object(eventlet, 'sleep')
self.driver.do_setup('context') self.driver.do_setup('context')
expected_properties = '%(host)s:%(port)s,1 %(name)s %(tgt_lun)s' % { expected_properties = '%(host)s:%(port)s,1 %(name)s %(tgt_lun)s' % {
@ -2109,6 +2134,9 @@ class QnapDriverVolumeTestCase(QnapDriverBaseTestCase):
'1.2.3.4', '1.2.3.4',
'Pool1', 'Pool1',
True)) True))
self.driver.configuration.use_chap_auth = False
self.driver.configuration.chap_username = ''
self.driver.configuration.chap_password = ''
self.driver.iscsi_port = 'fakeServicePort' self.driver.iscsi_port = 'fakeServicePort'
self.driver.do_setup('context') self.driver.do_setup('context')
@ -2332,7 +2360,7 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
self.assertEqual( self.assertEqual(
'fakeLunIndex', 'fakeLunIndex',
self.driver.api_executor.create_lun( self.driver.api_executor.create_lun(
fake_volume, 'fakepool', 'fakeLun', True)) fake_volume, 'fakepool', 'fakeLun', True, False, True, False))
fake_params = {} fake_params = {}
fake_params['func'] = 'add_lun' fake_params['func'] = 'add_lun'
@ -2342,6 +2370,8 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
fake_params['LUNPath'] = 'fakeLun' fake_params['LUNPath'] = 'fakeLun'
fake_params['poolID'] = 'fakepool' fake_params['poolID'] = 'fakepool'
fake_params['lv_ifssd'] = 'no' fake_params['lv_ifssd'] = 'no'
fake_params['compression'] = '1'
fake_params['dedup'] = 'off'
fake_params['LUNCapacity'] = 100 fake_params['LUNCapacity'] = 100
fake_params['lv_threshold'] = '80' fake_params['lv_threshold'] = '80'
fake_params['sid'] = 'fakeSid' fake_params['sid'] = 'fakeSid'
@ -2392,7 +2422,7 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
self.assertEqual( self.assertEqual(
'fakeLunIndex', 'fakeLunIndex',
self.driver.api_executor.create_lun( self.driver.api_executor.create_lun(
fake_volume, 'fakepool', 'fakeLun', False)) fake_volume, 'fakepool', 'fakeLun', False, False, True, False))
fake_params = {} fake_params = {}
fake_params['func'] = 'add_lun' fake_params['func'] = 'add_lun'
@ -2402,6 +2432,8 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
fake_params['LUNPath'] = 'fakeLun' fake_params['LUNPath'] = 'fakeLun'
fake_params['poolID'] = 'fakepool' fake_params['poolID'] = 'fakepool'
fake_params['lv_ifssd'] = 'no' fake_params['lv_ifssd'] = 'no'
fake_params['compression'] = '1'
fake_params['dedup'] = 'off'
fake_params['LUNCapacity'] = 100 fake_params['LUNCapacity'] = 100
fake_params['lv_threshold'] = '80' fake_params['lv_threshold'] = '80'
fake_params['sid'] = 'fakeSid' fake_params['sid'] = 'fakeSid'
@ -2453,7 +2485,8 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor.create_lun, self.driver.api_executor.create_lun,
fake_volume, 'fakepool', 'fakeLun', 'False') fake_volume, 'fakepool', 'fakeLun', 'False',
'False', 'True', 'False')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_create_lun_negative_with_wrong_result( def test_create_lun_negative_with_wrong_result(
@ -2481,7 +2514,8 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor.create_lun, self.driver.api_executor.create_lun,
fake_volume, 'fakepool', 'fakeLun', 'False') fake_volume, 'fakepool', 'fakeLun', 'False',
'False', 'True', 'False')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_delete_lun( def test_delete_lun(
@ -2845,7 +2879,7 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
True)) True))
self.driver.do_setup('context') self.driver.do_setup('context')
self.driver.api_executor.add_target_init( self.driver.api_executor.add_target_init(
'fakeTargetIqn', 'fakeInitiatorIqn') 'fakeTargetIqn', 'fakeInitiatorIqn', False, '', '')
fake_params = {} fake_params = {}
fake_params['func'] = 'add_init' fake_params['func'] = 'add_init'
@ -2905,7 +2939,7 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
self.driver.do_setup('context') self.driver.do_setup('context')
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor.add_target_init, self.driver.api_executor.add_target_init,
'fakeTargetIqn', 'fakeInitiatorIqn') 'fakeTargetIqn', 'fakeInitiatorIqn', False, '', '')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_add_target_init_negative_with_wrong_result( def test_add_target_init_negative_with_wrong_result(
@ -2929,7 +2963,7 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
self.driver.do_setup('context') self.driver.do_setup('context')
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor.add_target_init, self.driver.api_executor.add_target_init,
'fakeTargetIqn', 'fakeInitiatorIqn') 'fakeTargetIqn', 'fakeInitiatorIqn', False, '', '')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_remove_target_init( def test_remove_target_init(
@ -4531,7 +4565,7 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
True)) True))
self.driver.do_setup('context') self.driver.do_setup('context')
self.driver.api_executor.get_target_info_by_initiator( self.driver.api_executor.get_target_info_by_initiator(
'fakeInitiatorIQN', 'fakeLunSlotId') 'fakeInitiatorIQN')
fake_params = {} fake_params = {}
fake_params['func'] = 'extra_get' fake_params['func'] = 'extra_get'
@ -4582,7 +4616,7 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor. self.driver.api_executor.
get_target_info_by_initiator, get_target_info_by_initiator,
'fakeInitiatorIQN', 'fakeLunSlotId') 'fakeInitiatorIQN')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_get_target_info_by_initiator_with_wrong_result( def test_get_target_info_by_initiator_with_wrong_result(
@ -4605,7 +4639,7 @@ class QnapAPIExecutorEsTestCase(QnapDriverBaseTestCase):
True)) True))
self.driver.do_setup('context') self.driver.do_setup('context')
self.driver.api_executor.get_target_info_by_initiator( self.driver.api_executor.get_target_info_by_initiator(
'fakeInitiatorIQN', 'fakeLunSlotId') 'fakeInitiatorIQN')
fake_params = {} fake_params = {}
fake_params['func'] = 'extra_get' fake_params['func'] = 'extra_get'
@ -4662,7 +4696,7 @@ class QnapAPIExecutorTsTestCase(QnapDriverBaseTestCase):
self.assertEqual( self.assertEqual(
'fakeLunIndex', 'fakeLunIndex',
self.driver.api_executor.create_lun( self.driver.api_executor.create_lun(
fake_volume, 'fakepool', 'fakeLun', True)) fake_volume, 'fakepool', 'fakeLun', True, False, True, False))
fake_params = {} fake_params = {}
fake_params['func'] = 'add_lun' fake_params['func'] = 'add_lun'
@ -4722,7 +4756,7 @@ class QnapAPIExecutorTsTestCase(QnapDriverBaseTestCase):
self.assertEqual( self.assertEqual(
'fakeLunIndex', 'fakeLunIndex',
self.driver.api_executor.create_lun( self.driver.api_executor.create_lun(
fake_volume, 'fakepool', 'fakeLun', False)) fake_volume, 'fakepool', 'fakeLun', False, False, True, False))
fake_params = {} fake_params = {}
fake_params['func'] = 'add_lun' fake_params['func'] = 'add_lun'
@ -4783,7 +4817,8 @@ class QnapAPIExecutorTsTestCase(QnapDriverBaseTestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor.create_lun, self.driver.api_executor.create_lun,
fake_volume, 'fakepool', 'fakeLun', 'False') fake_volume, 'fakepool', 'fakeLun', 'False',
'False', 'True', 'False')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_create_lun_negative_with_wrong_result( def test_create_lun_negative_with_wrong_result(
@ -4811,7 +4846,8 @@ class QnapAPIExecutorTsTestCase(QnapDriverBaseTestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor.create_lun, self.driver.api_executor.create_lun,
fake_volume, 'fakepool', 'fakeLun', 'False') fake_volume, 'fakepool', 'fakeLun', 'False',
'False', 'True', 'False')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_delete_lun( def test_delete_lun(
@ -5742,7 +5778,7 @@ class QnapAPIExecutorTesTestCase(QnapDriverBaseTestCase):
self.assertEqual( self.assertEqual(
'fakeLunIndex', 'fakeLunIndex',
self.driver.api_executor.create_lun( self.driver.api_executor.create_lun(
fake_volume, 'fakepool', 'fakeLun', True)) fake_volume, 'fakepool', 'fakeLun', True, False, True, False))
fake_params = {} fake_params = {}
fake_params['func'] = 'add_lun' fake_params['func'] = 'add_lun'
@ -5752,6 +5788,8 @@ class QnapAPIExecutorTesTestCase(QnapDriverBaseTestCase):
fake_params['LUNPath'] = 'fakeLun' fake_params['LUNPath'] = 'fakeLun'
fake_params['poolID'] = 'fakepool' fake_params['poolID'] = 'fakepool'
fake_params['lv_ifssd'] = 'no' fake_params['lv_ifssd'] = 'no'
fake_params['compression'] = '1'
fake_params['dedup'] = 'off'
fake_params['sync'] = 'disabled' fake_params['sync'] = 'disabled'
fake_params['LUNCapacity'] = 100 fake_params['LUNCapacity'] = 100
fake_params['lv_threshold'] = '80' fake_params['lv_threshold'] = '80'
@ -5803,7 +5841,7 @@ class QnapAPIExecutorTesTestCase(QnapDriverBaseTestCase):
self.assertEqual( self.assertEqual(
'fakeLunIndex', 'fakeLunIndex',
self.driver.api_executor.create_lun( self.driver.api_executor.create_lun(
fake_volume, 'fakepool', 'fakeLun', False)) fake_volume, 'fakepool', 'fakeLun', False, False, True, False))
fake_params = {} fake_params = {}
fake_params['func'] = 'add_lun' fake_params['func'] = 'add_lun'
@ -5813,6 +5851,8 @@ class QnapAPIExecutorTesTestCase(QnapDriverBaseTestCase):
fake_params['LUNPath'] = 'fakeLun' fake_params['LUNPath'] = 'fakeLun'
fake_params['poolID'] = 'fakepool' fake_params['poolID'] = 'fakepool'
fake_params['lv_ifssd'] = 'no' fake_params['lv_ifssd'] = 'no'
fake_params['compression'] = '1'
fake_params['dedup'] = 'off'
fake_params['sync'] = 'disabled' fake_params['sync'] = 'disabled'
fake_params['LUNCapacity'] = 100 fake_params['LUNCapacity'] = 100
fake_params['lv_threshold'] = '80' fake_params['lv_threshold'] = '80'
@ -5865,7 +5905,8 @@ class QnapAPIExecutorTesTestCase(QnapDriverBaseTestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor.create_lun, self.driver.api_executor.create_lun,
fake_volume, 'fakepool', 'fakeLun', 'False') fake_volume, 'fakepool', 'fakeLun', 'False',
'False', 'True', 'False')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_create_lun_negative_with_wrong_result( def test_create_lun_negative_with_wrong_result(
@ -5893,7 +5934,8 @@ class QnapAPIExecutorTesTestCase(QnapDriverBaseTestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.driver.api_executor.create_lun, self.driver.api_executor.create_lun,
fake_volume, 'fakepool', 'fakeLun', 'False') fake_volume, 'fakepool', 'fakeLun', 'False',
'False', 'True', 'False')
@mock.patch('six.moves.http_client.HTTPConnection') @mock.patch('six.moves.http_client.HTTPConnection')
def test_get_ethernet_ip_with_type( def test_get_ethernet_ip_with_type(

View File

@ -31,6 +31,7 @@ except ImportError:
from oslo_concurrency import lockutils from oslo_concurrency import lockutils
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import strutils
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_utils import units from oslo_utils import units
import six import six
@ -62,16 +63,23 @@ CONF.register_opts(qnap_opts, group=configuration.SHARED_CONF_GROUP)
@interface.volumedriver @interface.volumedriver
class QnapISCSIDriver(san.SanISCSIDriver): class QnapISCSIDriver(san.SanISCSIDriver):
"""OpenStack driver to enable QNAP Storage. """QNAP iSCSI based cinder driver
.. code-block:: default
Version History:
1.0.0:
Initial driver (Only iSCSI).
1.2.001:
Add supports for Thin Provisioning, SSD Cache, Deduplication
, Compression and CHAP.
Version history:
1.0.0 - Initial driver (Only iSCSI)
""" """
# ThirdPartySystems wiki page # ThirdPartySystems wiki page
CI_WIKI_NAME = "QNAP_CI" CI_WIKI_NAME = "QNAP_CI"
VERSION = '1.1.021' VERSION = '1.2.001'
TIME_INTERVAL = 3 TIME_INTERVAL = 3
@ -103,6 +111,22 @@ class QnapISCSIDriver(san.SanISCSIDriver):
raise exception.InvalidInput( raise exception.InvalidInput(
reason=_('%s is not set.') % attr) reason=_('%s is not set.') % attr)
if not self.configuration.use_chap_auth:
self.configuration.chap_username = ''
self.configuration.chap_password = ''
else:
if not str.isalnum(self.configuration.chap_username):
# invalid chap_username
LOG.error('Username must be single-byte alphabet or number.')
raise exception.InvalidInput(
reason=_('Username must be single-byte '
'alphabet or number.'))
if not 12 <= len(self.configuration.chap_password) <= 16:
# invalid chap_password
LOG.error('Password must contain 12-16 characters.')
raise exception.InvalidInput(
reason=_('Password must contain 12-16 characters.'))
def do_setup(self, context): def do_setup(self, context):
"""Setup the QNAP Cinder volume driver.""" """Setup the QNAP Cinder volume driver."""
self._check_config() self._check_config()
@ -111,7 +135,7 @@ class QnapISCSIDriver(san.SanISCSIDriver):
# Setup API Executor # Setup API Executor
try: try:
self.api_executor = self.creat_api_executor() self.api_executor = self.create_api_executor()
except Exception: except Exception:
LOG.error('Failed to create HTTP client. ' LOG.error('Failed to create HTTP client. '
'Check ip, port, username, password' 'Check ip, port, username, password'
@ -123,7 +147,7 @@ class QnapISCSIDriver(san.SanISCSIDriver):
"""Check the status of setup.""" """Check the status of setup."""
pass pass
def creat_api_executor(self): def create_api_executor(self):
"""Create api executor by nas model.""" """Create api executor by nas model."""
self.api_executor = QnapAPIExecutor( self.api_executor = QnapAPIExecutor(
username=self.configuration.san_login, username=self.configuration.san_login,
@ -229,12 +253,60 @@ class QnapISCSIDriver(san.SanISCSIDriver):
break break
return create_lun_name return create_lun_name
def _parse_boolean_extra_spec(self, extra_spec_value):
"""Parse boolean value from extra spec.
Parse extra spec values of the form '<is> True' , '<is> False',
'True' and 'False'.
"""
if not isinstance(extra_spec_value, six.string_types):
extra_spec_value = six.text_type(extra_spec_value)
match = re.match(r'^<is>\s*(?P<value>True|False)$',
extra_spec_value.strip(),
re.IGNORECASE)
if match:
extra_spec_value = match.group('value')
return strutils.bool_from_string(extra_spec_value, strict=True)
def create_volume(self, volume): def create_volume(self, volume):
"""Create a new volume.""" """Create a new volume."""
start_time = time.time() start_time = time.time()
LOG.debug('in create_volume') LOG.debug('in create_volume')
LOG.debug('volume: %s', volume.__dict__) LOG.debug('volume: %s', volume.__dict__)
reserve = self.configuration.san_thin_provision try:
extra_specs = volume["volume_type"]["extra_specs"]
LOG.debug('extra_spec: %s', extra_specs)
qnap_thin_provision = self._parse_boolean_extra_spec(
extra_specs.get('qnap_thin_provision', 'true'))
qnap_compression = self._parse_boolean_extra_spec(
extra_specs.get('qnap_compression', 'true'))
qnap_deduplication = self._parse_boolean_extra_spec(
extra_specs.get('qnap_deduplication', 'false'))
qnap_ssd_cache = self._parse_boolean_extra_spec(
extra_specs.get('qnap_ssd_cache', 'false'))
except TypeError:
LOG.debug('Unable to retrieve extra specs info. '
'Use default extra spec.')
qnap_thin_provision = True
qnap_compression = True
qnap_deduplication = False
qnap_ssd_cache = False
LOG.debug('qnap_thin_provision: %(qnap_thin_provision)s '
'qnap_compression: %(qnap_compression)s '
'qnap_deduplication: %(qnap_deduplication)s '
'qnap_ssd_cache: %(qnap_ssd_cache)s',
{'qnap_thin_provision': qnap_thin_provision,
'qnap_compression': qnap_compression,
'qnap_deduplication': qnap_deduplication,
'qnap_ssd_cache': qnap_ssd_cache})
if (qnap_deduplication and not qnap_thin_provision):
LOG.debug('Dedupe cannot be enabled without thin_provisioning.')
raise exception.VolumeBackendAPIException(
data=_('Dedupe cannot be enabled without thin_provisioning.'))
# User could create two volume with the same name on horizon. # User could create two volume with the same name on horizon.
# Therefore, We should not use display name to create lun on nas. # Therefore, We should not use display name to create lun on nas.
@ -244,7 +316,10 @@ class QnapISCSIDriver(san.SanISCSIDriver):
volume, volume,
self.configuration.qnap_poolname, self.configuration.qnap_poolname,
create_lun_name, create_lun_name,
reserve) qnap_thin_provision,
qnap_ssd_cache,
qnap_compression,
qnap_deduplication)
max_wait_sec = 600 max_wait_sec = 600
try_times = 0 try_times = 0
@ -590,7 +665,11 @@ class QnapISCSIDriver(san.SanISCSIDriver):
free_capacity_gb=freesize_bytes / units.Gi, free_capacity_gb=freesize_bytes / units.Gi,
provisioned_capacity_gb=provisioned_bytes / units.Gi, provisioned_capacity_gb=provisioned_bytes / units.Gi,
reserved_percentage=self.configuration.reserved_percentage, reserved_percentage=self.configuration.reserved_percentage,
QoS_support=False) QoS_support=False,
qnap_thin_provision=["True", "False"],
qnap_compression=["True", "False"],
qnap_deduplication=["True", "False"],
qnap_ssd_cache=["True", "False"])
self.group_stats['pools'] = [single_pool] self.group_stats['pools'] = [single_pool]
return self.group_stats return self.group_stats
@ -627,51 +706,6 @@ class QnapISCSIDriver(san.SanISCSIDriver):
target_index = '' target_index = ''
target_iqn = '' target_iqn = ''
if 'ES' in internal_model_name.upper() and fw_version == "1.1.4":
LOG.debug('in ES FW after 1.1.4: get_target_info_by_initiator')
target_index, target_iqn = (self.api_executor
.get_target_info_by_initiator
(connector['initiator'], lun_slot_id))
LOG.debug('get_target_info_by_initiator target_index: %s',
target_index)
LOG.debug('get_target_info_by_initiator target_iqn: %s',
target_iqn)
else:
ret = self.api_executor.get_all_iscsi_portal_setting()
root = ET.fromstring(ret['data'])
# find the targets have acl with connector['initiator']
target_with_initiator_list = []
target_acl_tree = root.find('targetACL')
target_acl_list = target_acl_tree.findall('row')
tmp_target_iqn = ''
for targetACL in target_acl_list:
tmp_target_iqn = targetACL.find('targetIQN').text
# If lun and the targetiqn in different controller,
# skip the targetiqn, in case lun in sca map to target of scb
LOG.debug('lun_slot_id: %s', lun_slot_id)
LOG.debug('tmp_target_iqn[-1]: %s', tmp_target_iqn[-1])
if (lun_slot_id != ''):
if (lun_slot_id != tmp_target_iqn[-1]):
LOG.debug('skip the targetiqn')
continue
target_init_info_list = targetACL.findall('targetInitInfo')
for targetInitInfo in target_init_info_list:
if(targetInitInfo.find('initiatorIQN').text ==
connector['initiator']):
target_with_initiator_list.append(
targetACL.find('targetIndex').text)
# find the target in target_with_initiator_list with ready status
target_tree = root.find('iSCSITargetList')
target_list = target_tree.findall('targetInfo')
for target_with_initiator in target_with_initiator_list:
for target in target_list:
if(target_with_initiator ==
target.find('targetIndex').text):
if int(target.find('targetStatus').text) >= 0:
target_index = target_with_initiator
target_iqn = target.find('targetIQN').text
# create a new target if no target has ACL connector['initiator'] # create a new target if no target has ACL connector['initiator']
LOG.debug('exist target_index: %s', target_index) LOG.debug('exist target_index: %s', target_index)
@ -703,7 +737,10 @@ class QnapISCSIDriver(san.SanISCSIDriver):
self.api_executor.remove_target_init(target_iqn, default_acl) self.api_executor.remove_target_init(target_iqn, default_acl)
# add ACL # add ACL
self.api_executor.add_target_init( self.api_executor.add_target_init(
target_iqn, connector['initiator']) target_iqn, connector['initiator'],
self.configuration.use_chap_auth,
self.configuration.chap_username,
self.configuration.chap_password)
# Get information for multipath # Get information for multipath
target_iqns = [] target_iqns = []
@ -799,11 +836,7 @@ class QnapISCSIDriver(san.SanISCSIDriver):
LOG.debug('connector: %s', connector['initiator']) LOG.debug('connector: %s', connector['initiator'])
iscsi_port, target_index, target_iqn, target_iqns, target_portals = ( iscsi_port, target_index, target_iqn, target_iqns, target_portals = (
Util.retriveFormCache(connector['initiator'] + self._get_portal_info(volume, connector, lun_slot_id, lun_owner))
self.configuration.qnap_management_url,
lambda: self._get_portal_info(
volume, connector, lun_slot_id, lun_owner),
30))
self.api_executor.map_lun(lun_index, target_index) self.api_executor.map_lun(lun_index, target_index)
@ -882,6 +915,12 @@ class QnapISCSIDriver(san.SanISCSIDriver):
'tgt_lun': target_lun_id, 'tgt_lun': target_lun_id,
} }
if self.configuration.use_chap_auth:
provider_auth = 'CHAP %s %s' % (self.configuration.chap_username,
self.configuration.chap_password)
else:
provider_auth = None
elapsed_time = time.time() - start_time elapsed_time = time.time() - start_time
LOG.debug('create_export elapsed_time: %s', elapsed_time) LOG.debug('create_export elapsed_time: %s', elapsed_time)
@ -890,7 +929,7 @@ class QnapISCSIDriver(san.SanISCSIDriver):
return ( return (
{'provider_location': provider_location, {'provider_location': provider_location,
'provider_auth': None}) 'provider_auth': provider_auth})
def initialize_connection(self, volume, connector): def initialize_connection(self, volume, connector):
start_time = time.time() start_time = time.time()
@ -921,6 +960,11 @@ class QnapISCSIDriver(san.SanISCSIDriver):
properties['target_lun'] = target_lun_id properties['target_lun'] = target_lun_id
properties['volume_id'] = volume['id'] # used by xen currently properties['volume_id'] = volume['id'] # used by xen currently
if self.configuration.use_chap_auth:
properties['auth_method'] = 'CHAP'
properties['auth_username'] = self.configuration.chap_username
properties['auth_password'] = self.configuration.chap_password
elapsed_time = time.time() - start_time elapsed_time = time.time() - start_time
LOG.debug('initialize_connection elapsed_time: %s', elapsed_time) LOG.debug('initialize_connection elapsed_time: %s', elapsed_time)
@ -1007,6 +1051,7 @@ class QnapISCSIDriver(san.SanISCSIDriver):
elapsed_time = time.time() - start_time elapsed_time = time.time() - start_time
LOG.debug('terminate_connection elapsed_time : %s', elapsed_time) LOG.debug('terminate_connection elapsed_time : %s', elapsed_time)
self.api_executor.delete_target(target_index)
def update_migrated_volume( def update_migrated_volume(
self, context, volume, new_volume, original_volume_status): self, context, volume, new_volume, original_volume_status):
@ -1217,7 +1262,8 @@ class QnapAPIExecutor(object):
return res_details return res_details
@_connection_checker @_connection_checker
def create_lun(self, volume, pool_name, create_lun_name, reserve): def create_lun(self, volume, pool_name, create_lun_name, reserve,
ssd_cache, compress, dedup):
"""Create lun.""" """Create lun."""
self.es_create_lun_lock.acquire() self.es_create_lun_lock.acquire()
@ -1236,7 +1282,9 @@ class QnapAPIExecutor(object):
LUNName=create_lun_name, LUNName=create_lun_name,
LUNPath=create_lun_name, LUNPath=create_lun_name,
poolID=pool_name, poolID=pool_name,
lv_ifssd='no', lv_ifssd='yes' if ssd_cache else 'no',
compression='1' if compress else '0',
dedup='sha256' if dedup else 'off',
LUNCapacity=volume['size'], LUNCapacity=volume['size'],
lv_threshold='80', lv_threshold='80',
sid=self.sid) sid=self.sid)
@ -1337,7 +1385,24 @@ class QnapAPIExecutor(object):
return targetIndex return targetIndex
@_connection_checker @_connection_checker
def add_target_init(self, target_iqn, init_iqn): def delete_target(self, target_index):
"""Delete target on nas."""
res_details = self._get_res_details(
'/cgi-bin/disk/iscsi_target_setting.cgi?',
func='remove_target',
targetIndex=target_index,
sid=self.sid)
root = ET.fromstring(res_details['data'])
if root.find('authPassed').text == '0':
raise exception.VolumeBackendAPIException(
data=_('Session id expired'))
if root.find('result').text != '0':
raise exception.VolumeBackendAPIException(
data=_('Delete target failed'))
@_connection_checker
def add_target_init(self, target_iqn, init_iqn, use_chap_auth,
chap_username, chap_password):
"""Add target acl.""" """Add target acl."""
res_details = self._get_res_details( res_details = self._get_res_details(
'/cgi-bin/disk/iscsi_target_setting.cgi?', '/cgi-bin/disk/iscsi_target_setting.cgi?',
@ -1345,9 +1410,9 @@ class QnapAPIExecutor(object):
targetIQN=target_iqn, targetIQN=target_iqn,
initiatorIQN=init_iqn, initiatorIQN=init_iqn,
initiatorAlias=init_iqn, initiatorAlias=init_iqn,
bCHAPEnable='0', bCHAPEnable='1' if use_chap_auth else '0',
CHAPUserName='', CHAPUserName=chap_username,
CHAPPasswd='', CHAPPasswd=chap_password,
bMutualCHAPEnable='0', bMutualCHAPEnable='0',
mutualCHAPUserName='', mutualCHAPUserName='',
mutualCHAPPasswd='', mutualCHAPPasswd='',
@ -1694,12 +1759,12 @@ class QnapAPIExecutor(object):
return target return target
@_connection_checker @_connection_checker
def get_target_info_by_initiator(self, initiator_iqn, lun_slot_id): def get_target_info_by_initiator(self, initiatorIQN):
"""Get target info by initiatorIQN.""" """Get target info by initiatorIQN."""
res_details = self._get_res_details( res_details = self._get_res_details(
'/cgi-bin/disk/iscsi_portal_setting.cgi?', '/cgi-bin/disk/iscsi_portal_setting.cgi?',
func='extra_get', func='extra_get',
initiatorIQN=initiator_iqn, initiatorIQN=initiatorIQN,
sid=self.sid) sid=self.sid)
root = ET.fromstring(res_details['data']) root = ET.fromstring(res_details['data'])
@ -1709,22 +1774,11 @@ class QnapAPIExecutor(object):
if root.find('result').text < '0': if root.find('result').text < '0':
return "", "" return "", ""
target_acl_tree = root.find('targetACL') target = root.find('targetACL').find('row')
target_acl_list = target_acl_tree.findall('row') targetIndex = target.find('targetIndex').text
for target_acl in target_acl_list: targetIQN = target.find('targetIQN').text
target_iqn = target_acl.find('targetIQN').text
# If lun and the targetiqn in different controller, return targetIndex, targetIQN
# skip the targetiqn, in case lun in sca map to target of scb
LOG.debug('lun_slot_id: %s', lun_slot_id)
LOG.debug('target_iqn[-1]: %s', target_iqn[-1])
if (lun_slot_id != ''):
if (lun_slot_id != target_iqn[-1]):
LOG.debug('skip the targetiqn')
continue
target_index = target_acl.find('targetIndex').text
if int(target_acl.find('targetStatus').text) >= 0:
return target_index, target_iqn
return "", ""
class QnapAPIExecutorTS(QnapAPIExecutor): class QnapAPIExecutorTS(QnapAPIExecutor):
@ -1734,7 +1788,8 @@ class QnapAPIExecutorTS(QnapAPIExecutor):
lun_locks = {} lun_locks = {}
@_connection_checker @_connection_checker
def create_lun(self, volume, pool_name, create_lun_name, reserve): def create_lun(self, volume, pool_name, create_lun_name, reserve,
ssd_cache, compress, dedup):
"""Create lun.""" """Create lun."""
self.create_lun_lock.acquire() self.create_lun_lock.acquire()
@ -1753,7 +1808,7 @@ class QnapAPIExecutorTS(QnapAPIExecutor):
LUNName=create_lun_name, LUNName=create_lun_name,
LUNPath=create_lun_name, LUNPath=create_lun_name,
poolID=pool_name, poolID=pool_name,
lv_ifssd='no', lv_ifssd='yes' if ssd_cache else 'no',
LUNCapacity=volume['size'], LUNCapacity=volume['size'],
lv_threshold='80', lv_threshold='80',
sid=self.sid) sid=self.sid)
@ -2006,13 +2061,30 @@ class QnapAPIExecutorTS(QnapAPIExecutor):
targetIndex = root.find('result').text targetIndex = root.find('result').text
return targetIndex return targetIndex
@_connection_checker
def delete_target(self, target_index):
"""Delete target on nas."""
res_details = self._get_res_details(
'/cgi-bin/disk/iscsi_target_setting.cgi?',
func='remove_target',
targetIndex=target_index,
sid=self.sid)
root = ET.fromstring(res_details['data'])
if root.find('authPassed').text == '0':
raise exception.VolumeBackendAPIException(
data=_('Session id expired'))
if root.find('result').text != target_index:
raise exception.VolumeBackendAPIException(
data=_('Delete target failed'))
class QnapAPIExecutorTES(QnapAPIExecutor): class QnapAPIExecutorTES(QnapAPIExecutor):
"""Makes QNAP API calls for TES NAS.""" """Makes QNAP API calls for TES NAS."""
tes_create_lun_lock = threading.Lock() tes_create_lun_lock = threading.Lock()
@_connection_checker @_connection_checker
def create_lun(self, volume, pool_name, create_lun_name, reserve): def create_lun(self, volume, pool_name, create_lun_name, reserve,
ssd_cache, compress, dedup):
"""Create lun.""" """Create lun."""
self.tes_create_lun_lock.acquire() self.tes_create_lun_lock.acquire()
@ -2031,7 +2103,9 @@ class QnapAPIExecutorTES(QnapAPIExecutor):
LUNName=create_lun_name, LUNName=create_lun_name,
LUNPath=create_lun_name, LUNPath=create_lun_name,
poolID=pool_name, poolID=pool_name,
lv_ifssd='no', lv_ifssd='yes' if ssd_cache else 'no',
compression='1' if compress else '0',
dedup='sha256' if dedup else 'off',
sync='disabled', sync='disabled',
LUNCapacity=volume['size'], LUNCapacity=volume['size'],
lv_threshold='80', lv_threshold='80',

View File

@ -0,0 +1,5 @@
---
features:
- |
Add enhanced support to the QNAP Cinder driver, including
'CHAP', 'Thin Provision', 'SSD Cache', 'Dedup' and 'Compression'.