diff --git a/doc/source/admin/share_back_ends_feature_support_mapping.rst b/doc/source/admin/share_back_ends_feature_support_mapping.rst index bc16a14cc0..d929afdf4a 100644 --- a/doc/source/admin/share_back_ends_feature_support_mapping.rst +++ b/doc/source/admin/share_back_ends_feature_support_mapping.rst @@ -272,7 +272,7 @@ More information: :ref:`capabilities_and_extra_specs` +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+ | MapRFS | \- | N | \- | \- | \- | N | \- | O | \- | \- | P | \- | +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+ -| QNAP | \- | O | \- | \- | O | \- | \- | O | \- | \- | P | \- | +| QNAP | \- | O | Q | Q | O | Q | \- | O | \- | \- | P | \- | +----------------------------------------+-----------+------------+--------+-------------+-------------------+--------------------+-----+----------------------------+--------------------+--------------------+--------------+--------------+ .. note:: diff --git a/manila/share/drivers/qnap/api.py b/manila/share/drivers/qnap/api.py index 8a8a355e43..46c1e39686 100644 --- a/manila/share/drivers/qnap/api.py +++ b/manila/share/drivers/qnap/api.py @@ -184,7 +184,8 @@ class QnapAPIExecutor(object): return sanitized_params @_connection_checker - def create_share(self, share, pool_name, create_share_name, share_proto): + def create_share(self, share, pool_name, create_share_name, + share_proto, **kwargs): """Create share.""" LOG.debug('create_share_name: %s', create_share_name) @@ -194,10 +195,12 @@ class QnapAPIExecutor(object): 'vol_name': create_share_name, 'vol_size': six.text_type(share['size']) + 'GB', 'threshold': '80', - 'dedup': 'off', - 'compression': '1', - 'thin_pro': '0', - 'cache': '0', + 'dedup': ('sha512' + if kwargs['qnap_deduplication'] is True + else 'off'), + 'compression': '1' if kwargs['qnap_compression'] is True else '0', + 'thin_pro': '1' if kwargs['qnap_thin_provision'] is True else '0', + 'cache': '1' if kwargs['qnap_ssd_cache'] is True else '0', 'cifs_enable': '0' if share_proto == 'NFS' else '1', 'nfs_enable': '0' if share_proto == 'CIFS' else '1', 'afp_enable': '0', @@ -497,10 +500,10 @@ class QnapAPIExecutor(object): 'sharename': share_dict['sharename'], 'old_sharename': share_dict['old_sharename'], 'vol_size': six.text_type(share_dict['new_size']) + 'GB', - 'dedup': '0', - 'compression': '1', - 'thin_pro': '0', - 'cache': '0', + 'dedup': 'sha512' if share_dict['deduplication'] else 'off', + 'compression': '1' if share_dict['compression'] else '0', + 'thin_pro': '1' if share_dict['thin_provision'] else '0', + 'cache': '1' if share_dict['ssd_cache'] else '0', 'cifs_enable': '1' if share_dict['share_proto'] == 'CIFS' else '0', 'nfs_enable': '1' if share_dict['share_proto'] == 'NFS' else '0', 'afp_enable': '0', diff --git a/manila/share/drivers/qnap/qnap.py b/manila/share/drivers/qnap/qnap.py index ec4ab1b3eb..1a67a0eb4d 100644 --- a/manila/share/drivers/qnap/qnap.py +++ b/manila/share/drivers/qnap/qnap.py @@ -31,6 +31,7 @@ from manila.i18n import _ from manila import share from manila.share import driver from manila.share.drivers.qnap import api +from manila.share import share_types from manila import utils LOG = logging.getLogger(__name__) @@ -66,9 +67,11 @@ class QnapShareDriver(driver.ShareDriver): 1.0.1 - Add support for QES fw 1.1.4. 1.0.2 - Fix bug #1736370, QNAP Manila driver: Access rule setting is override by the another access rule. + 1.0.3 - Add supports for Thin Provisioning, SSD Cache, Deduplication + and Compression. """ - DRIVER_VERSION = '1.0.2' + DRIVER_VERSION = '1.0.3' def __init__(self, *args, **kwargs): """Initialize QnapShareDriver.""" @@ -254,6 +257,10 @@ class QnapShareDriver(driver.ShareDriver): "allocated_capacity_gb": alloc_capacity_gb, "reserved_percentage": reserved_percentage, "qos": False, + "dedupe": [True, False], + "compression": [True, False], + "thin_provisioning": [True, False], + "qnap_ssd_cache": [True, False] } data = { @@ -276,6 +283,28 @@ class QnapShareDriver(driver.ShareDriver): def create_share(self, context, share, share_server=None): """Create a new share.""" LOG.debug('share: %s', share.__dict__) + extra_specs = share_types.get_extra_specs_from_share(share) + LOG.debug('extra_specs: %s', extra_specs) + qnap_thin_provision = share_types.parse_boolean_extra_spec( + 'thin_provisioning', extra_specs.get("thin_provisioning") or + extra_specs.get('capabilities:thin_provisioning') or 'true') + qnap_compression = share_types.parse_boolean_extra_spec( + 'compression', extra_specs.get("compression") or + extra_specs.get('capabilities:compression') or 'true') + qnap_deduplication = share_types.parse_boolean_extra_spec( + 'dedupe', extra_specs.get("dedupe") or + extra_specs.get('capabilities:dedupe') or 'false') + qnap_ssd_cache = share_types.parse_boolean_extra_spec( + 'qnap_ssd_cache', extra_specs.get("qnap_ssd_cache") or + extra_specs.get("capabilities:qnap_ssd_cache") or '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}) share_proto = share['share_proto'] @@ -293,11 +322,19 @@ class QnapShareDriver(driver.ShareDriver): LOG.error(msg) raise exception.ShareBackendException(msg=msg) + if (qnap_deduplication and not qnap_thin_provision): + msg = _("Dedupe cannot be enabled without thin_provisioning.") + LOG.debug('Dedupe cannot be enabled without thin_provisioning.') + raise exception.InvalidExtraSpec(reason=msg) self.api_executor.create_share( share, self.configuration.qnap_poolname, create_share_name, - share_proto) + share_proto, + qnap_thin_provision=qnap_thin_provision, + qnap_compression=qnap_compression, + qnap_deduplication=qnap_deduplication, + qnap_ssd_cache=qnap_ssd_cache) created_share = self._get_share_info(create_share_name) volID = created_share.find('vol_no').text # Use private_storage to record volume ID and Name created in the NAS. @@ -306,7 +343,11 @@ class QnapShareDriver(driver.ShareDriver): {'volID': volID, 'create_share_name': create_share_name}) _metadata = {'volID': volID, - 'volName': create_share_name} + 'volName': create_share_name, + 'thin_provision': qnap_thin_provision, + 'compression': qnap_compression, + 'deduplication': qnap_deduplication, + 'ssd_cache': qnap_ssd_cache} self.private_storage.update(share['id'], _metadata) return self._get_location_path(create_share_name, @@ -365,11 +406,27 @@ class QnapShareDriver(driver.ShareDriver): LOG.debug('Share %s does not exist', share['id']) raise exception.ShareResourceNotFound(share_id=share['id']) LOG.debug('volName: %s', volName) - + thin_provision = self.private_storage.get( + share['id'], 'thin_provision') + compression = self.private_storage.get(share['id'], 'compression') + deduplication = self.private_storage.get(share['id'], 'deduplication') + ssd_cache = self.private_storage.get(share['id'], 'ssd_cache') + LOG.debug('thin_provision: %(thin_provision)s ' + 'compression: %(compression)s ' + 'deduplication: %(deduplication)s ' + 'ssd_cache: %(ssd_cache)s', + {'thin_provision': thin_provision, + 'compression': compression, + 'deduplication': deduplication, + 'ssd_cache': ssd_cache}) share_dict = { 'sharename': volName, 'old_sharename': volName, 'new_size': new_size, + 'thin_provision': thin_provision == 'True', + 'compression': compression == 'True', + 'deduplication': deduplication == 'True', + 'ssd_cache': ssd_cache == 'True', 'share_proto': share['share_proto'] } self.api_executor.edit_share(share_dict) @@ -492,11 +549,32 @@ class QnapShareDriver(driver.ShareDriver): context, snapshot['share_instance']['share_id']) LOG.debug('snap_share[size]: %s', snap_share['size']) + thin_provision = self.private_storage.get( + snapshot['share_instance_id'], 'thin_provision') + compression = self.private_storage.get( + snapshot['share_instance_id'], 'compression') + deduplication = self.private_storage.get( + snapshot['share_instance_id'], 'deduplication') + ssd_cache = self.private_storage.get( + snapshot['share_instance_id'], 'ssd_cache') + LOG.debug('thin_provision: %(thin_provision)s ' + 'compression: %(compression)s ' + 'deduplication: %(deduplication)s ' + 'ssd_cache: %(ssd_cache)s', + {'thin_provision': thin_provision, + 'compression': compression, + 'deduplication': deduplication, + 'ssd_cache': ssd_cache}) + if (share['size'] > snap_share['size']): share_dict = { 'sharename': create_share_name, 'old_sharename': create_share_name, 'new_size': share['size'], + 'thin_provision': thin_provision == 'True', + 'compression': compression == 'True', + 'deduplication': deduplication == 'True', + 'ssd_cache': ssd_cache == 'True', 'share_proto': share['share_proto'] } self.api_executor.edit_share(share_dict) @@ -505,6 +583,10 @@ class QnapShareDriver(driver.ShareDriver): _metadata = { 'volID': create_volID, 'volName': create_share_name, + 'thin_provision': thin_provision, + 'compression': compression, + 'deduplication': deduplication, + 'ssd_cache': ssd_cache } self.private_storage.update(share['id'], _metadata) @@ -726,25 +808,62 @@ class QnapShareDriver(driver.ShareDriver): "backend.") % share['id'] raise exception.ManageInvalidShare(reason=msg) - _metadata = {} + extra_specs = share_types.get_extra_specs_from_share(share) + qnap_thin_provision = share_types.parse_boolean_extra_spec( + 'thin_provisioning', extra_specs.get("thin_provisioning") or + extra_specs.get('capabilities:thin_provisioning') or 'true') + qnap_compression = share_types.parse_boolean_extra_spec( + 'compression', extra_specs.get("compression") or + extra_specs.get('capabilities:compression') or 'true') + qnap_deduplication = share_types.parse_boolean_extra_spec( + 'dedupe', extra_specs.get("dedupe") or + extra_specs.get('capabilities:dedupe') or 'false') + qnap_ssd_cache = share_types.parse_boolean_extra_spec( + 'qnap_ssd_cache', extra_specs.get("qnap_ssd_cache") or + extra_specs.get("capabilities:qnap_ssd_cache") or '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): + msg = _("Dedupe cannot be enabled without thin_provisioning.") + LOG.debug('Dedupe cannot be enabled without thin_provisioning.') + raise exception.InvalidExtraSpec(reason=msg) + vol_no = existing_share.find('vol_no').text + vol = self.api_executor.get_specific_volinfo(vol_no) + vol_size_gb = int(vol.find('size').text) / units.Gi + + share_dict = { + 'sharename': share_name, + 'old_sharename': share_name, + 'new_size': vol_size_gb, + 'thin_provision': qnap_thin_provision, + 'compression': qnap_compression, + 'deduplication': qnap_deduplication, + 'ssd_cache': qnap_ssd_cache, + 'share_proto': share['share_proto'] + } + self.api_executor.edit_share(share_dict) + + _metadata = {} _metadata['volID'] = vol_no _metadata['volName'] = share_name + _metadata['thin_provision'] = qnap_thin_provision + _metadata['compression'] = qnap_compression + _metadata['deduplication'] = qnap_deduplication + _metadata['ssd_cache'] = qnap_ssd_cache self.private_storage.update(share['id'], _metadata) - # Test to get value from private_storage. - volID = self.private_storage.get(share['id'], 'volID') - LOG.debug('volID: %s', volID) - volName = self.private_storage.get(share['id'], 'volName') - LOG.debug('volName: %s', volName) - LOG.info("Share %(shr_path)s was successfully managed with ID " "%(shr_id)s.", {'shr_path': share['export_locations'][0]['path'], 'shr_id': share['id']}) - vol = self.api_executor.get_specific_volinfo(vol_no) - vol_size_gb = int(vol.find('size').text) / units.Gi export_locations = self._get_location_path( share_name, share['share_proto'], diff --git a/manila/tests/share/drivers/qnap/fakes.py b/manila/tests/share/drivers/qnap/fakes.py index 90fa364136..f9d4a2f2fa 100644 --- a/manila/tests/share/drivers/qnap/fakes.py +++ b/manila/tests/share/drivers/qnap/fakes.py @@ -138,6 +138,10 @@ FAKE_RES_DETAIL_DATA_VOLUME_INFO = """ fakeMountPath + + + + @@ -325,7 +329,8 @@ class SnapshotClass(object): 'id': 'fakeSnapshotId', 'share': {'share_id': 'fakeShareId', 'id': 'fakeId'}, 'share_instance': {'share_id': 'fakeShareId', 'id': 'fakeId'}, - 'size': self.size + 'size': self.size, + 'share_instance_id': 'fakeShareId' }[arg] def __setitem__(self, key, value): diff --git a/manila/tests/share/drivers/qnap/test_api.py b/manila/tests/share/drivers/qnap/test_api.py index 19c404a86c..e19a00c047 100644 --- a/manila/tests/share/drivers/qnap/test_api.py +++ b/manila/tests/share/drivers/qnap/test_api.py @@ -120,7 +120,11 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): self.share, 'Storage Pool 1', fake_name, - 'NFS') + 'NFS', + qnap_deduplication=False, + qnap_compression=True, + qnap_thin_provision=True, + qnap_ssd_cache=False) fake_params = { 'wiz_func': 'share_create', @@ -130,7 +134,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): 'threshold': '80', 'dedup': 'off', 'compression': '1', - 'thin_pro': '0', + 'thin_pro': '1', 'cache': '0', 'cifs_enable': '0', 'nfs_enable': '1', @@ -457,6 +461,10 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): "sharename": 'fakeVolId', "old_sharename": 'fakeVolId', "new_size": 100, + "deduplication": False, + "compression": True, + "thin_provision": True, + "ssd_cache": False, "share_proto": "NFS" } self.driver.api_executor.edit_share( @@ -468,9 +476,9 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): 'sharename': 'fakeVolId', 'old_sharename': 'fakeVolId', 'vol_size': '100GB', - 'dedup': '0', + 'dedup': 'off', 'compression': '1', - 'thin_pro': '0', + 'thin_pro': '1', 'cache': '0', 'cifs_enable': '0', 'nfs_enable': '1', @@ -736,7 +744,11 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): share=self.share, pool_name='Storage Pool 1', create_share_name='fake_share_name', - share_proto='NFS') + share_proto='NFS', + qnap_deduplication=False, + qnap_compression=True, + qnap_thin_provision=True, + qnap_ssd_cache=False) @ddt.unpack @ddt.data(['self.driver.api_executor.get_share_info', @@ -803,6 +815,10 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): {'share_dict': {"sharename": 'fakeVolId', "old_sharename": 'fakeVolId', "new_size": 100, + "deduplication": False, + "compression": True, + "thin_provision": False, + "ssd_cache": False, "share_proto": "NFS"}}, fakes.FakeEsResCodeNegativeResponse(), fakes.FakeGetBasicInfoResponseEs_1_1_3()], @@ -810,6 +826,10 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): {'share_dict': {"sharename": 'fakeVolId', "old_sharename": 'fakeVolId', "new_size": 100, + "deduplication": False, + "compression": True, + "thin_provision": False, + "ssd_cache": False, "share_proto": "NFS"}}, fakes.FakeAuthPassFailResponse(), fakes.FakeGetBasicInfoResponseEs_1_1_3()], diff --git a/manila/tests/share/drivers/qnap/test_qnap.py b/manila/tests/share/drivers/qnap/test_qnap.py index e5a861fc4f..8e5974a718 100644 --- a/manila/tests/share/drivers/qnap/test_qnap.py +++ b/manila/tests/share/drivers/qnap/test_qnap.py @@ -29,6 +29,7 @@ from eventlet import greenthread from manila import exception from manila.share.drivers.qnap import api from manila.share.drivers.qnap import qnap +from manila.share import share_types from manila import test from manila.tests import fake_share from manila.tests.share.drivers.qnap import fakes @@ -296,12 +297,72 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): return hosts + @ddt.data({ + 'fake_extra_spec': {}, + 'expect_extra_spec': { + 'qnap_thin_provision': True, + 'qnap_compression': True, + 'qnap_deduplication': False, + 'qnap_ssd_cache': False + } + }, { + 'fake_extra_spec': { + 'thin_provisioning': u'true', + 'compression': u'true', + 'qnap_ssd_cache': u'true' + }, + 'expect_extra_spec': { + 'qnap_thin_provision': True, + 'qnap_compression': True, + 'qnap_deduplication': False, + 'qnap_ssd_cache': True + } + }, { + 'fake_extra_spec': { + 'thin_provisioning': u' False', + 'compression': u' True', + 'qnap_ssd_cache': u' True' + }, + 'expect_extra_spec': { + 'qnap_thin_provision': False, + 'qnap_compression': True, + 'qnap_deduplication': False, + 'qnap_ssd_cache': True + } + }, { + 'fake_extra_spec': { + 'thin_provisioning': u'true', + 'dedupe': u' True', + 'qnap_ssd_cache': u'False' + }, + 'expect_extra_spec': { + 'qnap_thin_provision': True, + 'qnap_compression': True, + 'qnap_deduplication': True, + 'qnap_ssd_cache': False + } + }, { + 'fake_extra_spec': { + 'thin_provisioning': u' False', + 'compression': u'false', + 'dedupe': u' False', + 'qnap_ssd_cache': u' False' + }, + 'expect_extra_spec': { + 'qnap_thin_provision': False, + 'qnap_compression': False, + 'qnap_deduplication': False, + 'qnap_ssd_cache': False + } + }) + @ddt.unpack @mock.patch.object(qnap.QnapShareDriver, '_get_location_path') @mock.patch.object(qnap.QnapShareDriver, '_gen_random_name') def test_create_share_positive( self, mock_gen_random_name, - mock_get_location_path): + mock_get_location_path, + fake_extra_spec, expect_extra_spec): """Test create share.""" mock_api_executor = qnap.QnapShareDriver._create_api_executor mock_api_executor.return_value.get_share_info.side_effect = [ @@ -315,6 +376,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1', private_storage=mock_private_storage) + self.mock_object(share_types, 'get_extra_specs_from_share', + mock.Mock(return_value=fake_extra_spec)) self.driver.create_share('context', self.share) mock_api_return = mock_api_executor.return_value @@ -328,7 +391,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): self.share, self.driver.configuration.qnap_poolname, 'fakeShareName', - 'NFS') + 'NFS', + **expect_extra_spec) mock_get_location_path.assert_called_once_with( 'fakeShareName', 'NFS', '1.2.3.4', 'fakeNo') @@ -349,6 +413,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1', private_storage=mock_private_storage) + self.mock_object(share_types, 'get_extra_specs_from_share', + mock.Mock(return_value={})) self.assertRaises( exception.ShareBackendException, @@ -373,6 +439,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1', private_storage=mock_private_storage) + self.mock_object(share_types, 'get_extra_specs_from_share', + mock.Mock(return_value={})) self.assertRaises( exception.ShareBackendException, @@ -380,6 +448,34 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): context='context', share=self.share) + @mock.patch.object(qnap.QnapShareDriver, '_get_location_path') + @mock.patch.object(qnap.QnapShareDriver, '_gen_random_name') + def test_create_share_negative_configutarion( + self, + mock_gen_random_name, + mock_get_location_path): + """Test create share.""" + mock_api_executor = qnap.QnapShareDriver._create_api_executor + mock_api_executor.return_value.get_share_info.side_effect = [ + None, self.get_share_info_return_value()] + mock_gen_random_name.return_value = 'fakeShareName' + mock_get_location_path.return_value = None + mock_private_storage = mock.Mock() + + self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', + 'qnapadmin', 'Storage Pool 1', + private_storage=mock_private_storage) + self.mock_object(share_types, 'get_extra_specs_from_share', + mock.Mock(return_value={ + 'dedupe': 'true', + 'thin_provisioning': 'false'})) + + self.assertRaises( + exception.InvalidExtraSpec, + self.driver.create_share, + context='context', + share=self.share) + def test_delete_share_positive(self): """Test delete share with fake_share.""" mock_api_executor = qnap.QnapShareDriver._create_api_executor @@ -437,7 +533,12 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): self.get_share_info_return_value()) mock_api_executor.return_value.edit_share.return_value = None mock_private_storage = mock.Mock() - mock_private_storage.get.return_value = 'fakeVolName' + mock_private_storage.get.side_effect = [ + 'fakeVolName', + 'True', + 'True', + 'False', + 'False'] self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1', @@ -448,6 +549,10 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): 'sharename': 'fakeVolName', 'old_sharename': 'fakeVolName', 'new_size': 100, + 'thin_provision': True, + 'compression': True, + 'deduplication': False, + 'ssd_cache': False, 'share_proto': 'NFS' } mock_api_executor.return_value.edit_share.assert_called_once_with( @@ -611,7 +716,12 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): mock_api_executor.return_value.get_share_info.side_effect = [ None, self.get_share_info_return_value()] mock_private_storage = mock.Mock() - mock_private_storage.get.return_value = 'fakeVolName' + mock_private_storage.get.side_effect = [ + 'True', + 'True', + 'False', + 'False', + 'fakeVolName'] mock_share_api.return_value.get.return_value = {'size': 5} mock_api_executor.return_value.edit_share.return_value = ( None) @@ -637,6 +747,10 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): 'sharename': 'fakeShareName', 'old_sharename': 'fakeShareName', 'new_size': 10, + 'thin_provision': True, + 'compression': True, + 'deduplication': False, + 'ssd_cache': False, 'share_proto': 'NFS' } mock_api_return.edit_share.assert_called_once_with( @@ -816,6 +930,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1', private_storage=mock_private_storage) + self.mock_object(share_types, 'get_extra_specs_from_share', + mock.Mock(return_value={})) self.driver.manage_existing(self.share, 'driver_options') mock_api_return = mock_api_executor.return_value @@ -826,6 +942,39 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): mock_get_location_path.assert_called_once_with( 'fakeShareName', 'NFS', '1.2.3.4', 'fakeNo') + @mock.patch.object(qnap.QnapShareDriver, '_get_location_path') + def test_manage_existing_nfs_negative_configutarion( + self, + mock_get_location_path): + """Test manage existing.""" + mock_api_executor = qnap.QnapShareDriver._create_api_executor + mock_api_executor.return_value.get_share_info.return_value = ( + self.get_share_info_return_value()) + mock_private_storage = mock.Mock() + mock_private_storage.update.return_value = None + mock_private_storage.get.side_effect = [ + 'fakeVolId', + 'fakeVolName'] + mock_api_executor.return_value.get_specific_volinfo.return_value = ( + self.get_specific_volinfo_return_value()) + mock_api_executor.return_value.get_share_info.return_value = ( + self.get_share_info_return_value()) + mock_get_location_path.return_value = None + + self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', + 'qnapadmin', 'Storage Pool 1', + private_storage=mock_private_storage) + self.mock_object(share_types, 'get_extra_specs_from_share', + mock.Mock(return_value={ + 'dedupe': 'true', + 'thin_provisioning': 'false'})) + + self.assertRaises( + exception.InvalidExtraSpec, + self.driver.manage_existing, + share=self.share, + driver_options='driver_options') + def test_manage_invalid_protocol(self): """Test manage existing.""" share = fake_share.fake_share( diff --git a/releasenotes/notes/qnap-enhance-support-53848fda525b7ea4.yaml b/releasenotes/notes/qnap-enhance-support-53848fda525b7ea4.yaml new file mode 100644 index 0000000000..528424ceee --- /dev/null +++ b/releasenotes/notes/qnap-enhance-support-53848fda525b7ea4.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Added enhanced support to the QNAP Manila driver, including + ``Thin Provisioning``, ``SSD Cache``, ``Deduplication`` + and ``Compression``.