diff --git a/manila/share/drivers/qnap/api.py b/manila/share/drivers/qnap/api.py index e3aa7ea1a6..7d70208568 100644 --- a/manila/share/drivers/qnap/api.py +++ b/manila/share/drivers/qnap/api.py @@ -39,10 +39,10 @@ MSG_SESSION_EXPIRED = _("Session ID expired") MSG_UNEXPECT_RESP = _("Unexpected response from QNAP API") -@utils.retry(exception=exception.ShareBackendException, - retries=5) def _connection_checker(func): """Decorator to check session has expired or not.""" + @utils.retry(exception=exception.ShareBackendException, + retries=5) @functools.wraps(func) def inner_connection_checker(self, *args, **kwargs): LOG.debug('in _connection_checker') @@ -225,7 +225,8 @@ class QnapAPIExecutor(object): if root.find('authPassed').text == '0': raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED) if root.find('ES_RET_CODE').text < '0': - msg = _('Create share %s failed') % share['display_name'] + msg = _("Fail to create share %s on NAS.") % create_share_name + LOG.error(msg) raise exception.ShareBackendException(msg=msg) vol_list = root.find('func').find('ownContent').find('volumeList') @@ -319,26 +320,22 @@ class QnapAPIExecutor(object): if root.find('authPassed').text == '0': raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED) - if ('vol_no' in kwargs) or ('vol_label' in kwargs): - vol_list = root.find('Volume_Info') - vol_info_tree = vol_list.findall('row') - for vol in vol_info_tree: - LOG.debug('Iterating vol name: %(name)s, index: %(id)s', - {'name': vol.find('vol_label').text, - 'id': vol.find('vol_no').text}) - if 'vol_no' in kwargs: - if kwargs['vol_no'] == vol.find('vol_no').text: - LOG.debug('vol_no:%s', - vol.find('vol_no').text) - return vol - elif 'vol_label' in kwargs: - if kwargs['vol_label'] == vol.find('vol_label').text: - LOG.debug('vol_label:%s', vol.find('vol_label').text) - return vol - if vol is vol_info_tree[-1]: - return None - else: - return res_details['data'] + vol_list = root.find('Volume_Info') + vol_info_tree = vol_list.findall('row') + for vol in vol_info_tree: + LOG.debug('Iterating vol name: %(name)s, index: %(id)s', + {'name': vol.find('vol_label').text, + 'id': vol.find('vol_no').text}) + if 'vol_no' in kwargs: + if kwargs['vol_no'] == vol.find('vol_no').text: + LOG.debug('vol_no:%s', + vol.find('vol_no').text) + return vol + elif 'vol_label' in kwargs: + if kwargs['vol_label'] == vol.find('vol_label').text: + LOG.debug('vol_label:%s', vol.find('vol_label').text) + return vol + return None @_connection_checker def get_specific_volinfo(self, vol_id, **kwargs): @@ -453,9 +450,11 @@ class QnapAPIExecutor(object): raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED) # snapshot not exist if root.find('result').text == '-206021': + LOG.warning('Snapshot id %s does not exist', snapshot_id) return - # lun not exist + # share not exist if root.find('result').text == '-200005': + LOG.warning('Share of snapshot id %s does not exist', snapshot_id) return if root.find('result').text < '0': msg = _('Failed to delete snapshot.') @@ -499,8 +498,10 @@ class QnapAPIExecutor(object): 'compression': '1', 'thin_pro': '0', 'cache': '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', - 'ftp_enable': '1', + 'ftp_enable': '0', 'hidden': '0', 'oplocks': '1', 'sync': 'always', diff --git a/manila/share/drivers/qnap/qnap.py b/manila/share/drivers/qnap/qnap.py index 87e6df1c9b..fedde37c10 100644 --- a/manila/share/drivers/qnap/qnap.py +++ b/manila/share/drivers/qnap/qnap.py @@ -61,9 +61,10 @@ class QnapShareDriver(driver.ShareDriver): Version history: 1.0.0 - Initial driver (Only NFS) + 1.0.1 - Add support for QES fw 1.1.4. """ - DRIVER_VERSION = '1.0.0' + DRIVER_VERSION = '1.0.1' def __init__(self, *args, **kwargs): """Initialize QnapShareDriver.""" @@ -152,17 +153,14 @@ class QnapShareDriver(driver.ShareDriver): username=self.configuration.qnap_nas_login, password=self.configuration.qnap_nas_password, management_url=self.configuration.qnap_management_url) - - if (fw_version.startswith("1.1.2") or - fw_version.startswith("1.1.3")): + elif "1.1.2" <= fw_version <= "1.1.4": LOG.debug('Create ES API Executor') return api.QnapAPIExecutor( username=self.configuration.qnap_nas_login, password=self.configuration.qnap_nas_password, management_url=self.configuration.qnap_management_url) elif model_type in es_model_types: - if (fw_version.startswith("1.1.2") or - fw_version.startswith("1.1.3")): + if "1.1.2" <= fw_version <= "1.1.4": LOG.debug('Create ES API Executor') return api.QnapAPIExecutor( username=self.configuration.qnap_nas_login, @@ -195,13 +193,9 @@ class QnapShareDriver(driver.ShareDriver): {'ifx': infix, 'time': timeutils.utcnow().strftime('%Y%m%d%H%M%S%f')}) - def _get_location_path(self, share_name, share_proto, ip): + def _get_location_path(self, share_name, share_proto, ip, vol_id): if share_proto == 'NFS': - created_share = self.api_executor.get_share_info( - self.configuration.qnap_poolname, - vol_label=share_name) - vol_no = created_share.find('vol_no').text - vol = self.api_executor.get_specific_volinfo(vol_no) + vol = self.api_executor.get_specific_volinfo(vol_id) vol_mount_path = vol.find('vol_mount_path').text location = '%s:%s' % (ip, vol_mount_path) @@ -263,7 +257,8 @@ class QnapShareDriver(driver.ShareDriver): @utils.retry(exception=exception.ShareBackendException, interval=3, - retries=200) + retries=5) + @utils.synchronized('qnap-create_share') def create_share(self, context, share, share_server=None): """Create a new share.""" LOG.debug('share: %s', share.__dict__) @@ -277,25 +272,48 @@ class QnapShareDriver(driver.ShareDriver): created_share = self.api_executor.get_share_info( self.configuration.qnap_poolname, vol_label=create_share_name) - + LOG.debug('created_share: %s', created_share) if created_share is not None: - msg = _("Failed to create an unused share name.") + msg = (_("The share name %s is used by other share on NAS.") % + create_share_name) + LOG.error(msg) raise exception.ShareBackendException(msg=msg) - create_volID = self.api_executor.create_share( + self.api_executor.create_share( share, self.configuration.qnap_poolname, create_share_name, share_proto) - + 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. - _metadata = {'volID': create_volID, 'volName': create_share_name} + LOG.debug('volID: %(volID)s ' + 'volName: %(create_share_name)s', + {'volID': volID, + 'create_share_name': create_share_name}) + _metadata = {'volID': volID, + 'volName': create_share_name} self.private_storage.update(share['id'], _metadata) return self._get_location_path(create_share_name, share['share_proto'], - self.configuration.qnap_share_ip) + self.configuration.qnap_share_ip, + volID) + @utils.retry(exception=exception.ShareBackendException, + interval=5, retries=5, backoff_rate=1) + def _get_share_info(self, share_name): + share = self.api_executor.get_share_info( + self.configuration.qnap_poolname, + vol_label=share_name) + if share is None: + msg = _("Fail to get share info of %s on NAS.") % share_name + LOG.error(msg) + raise exception.ShareBackendException(msg=msg) + else: + return share + + @utils.synchronized('qnap-delete_share') def delete_share(self, context, share, share_server=None): """Delete the specified share.""" # Use private_storage to retrieve volume ID created in the NAS. @@ -317,11 +335,15 @@ class QnapShareDriver(driver.ShareDriver): self.api_executor.delete_share(vol_no) self.private_storage.delete(share['id']) + @utils.synchronized('qnap-extend_share') def extend_share(self, share, new_size, share_server=None): """Extend an existing share.""" - LOG.debug('Entering extend_share share=%(share)s ' + LOG.debug('Entering extend_share share_name=%(share_name)s ' + 'share_id=%(share_id)s ' 'new_size=%(size)s', - {'share': share['display_name'], 'size': new_size}) + {'share_name': share['display_name'], + 'share_id': share['id'], + 'size': new_size}) # Use private_storage to retrieve volume Name created in the NAS. volName = self.private_storage.get(share['id'], 'volName') @@ -331,15 +353,17 @@ class QnapShareDriver(driver.ShareDriver): LOG.debug('volName: %s', volName) share_dict = { - "sharename": volName, - "old_sharename": volName, - "new_size": new_size, + 'sharename': volName, + 'old_sharename': volName, + 'new_size': new_size, + 'share_proto': share['share_proto'] } self.api_executor.edit_share(share_dict) @utils.retry(exception=exception.ShareBackendException, interval=3, - retries=200) + retries=5) + @utils.synchronized('qnap-create_snapshot') def create_snapshot(self, context, snapshot, share_server=None): """Create a snapshot.""" LOG.debug('snapshot[share][share_id]: %s', @@ -393,6 +417,7 @@ class QnapShareDriver(driver.ShareDriver): return {'provider_location': snapshot_id} + @utils.synchronized('qnap-delete_snapshot') def delete_snapshot(self, context, snapshot, share_server=None): """Delete a snapshot.""" LOG.debug('Entering delete_snapshot. The deleted snapshot=%(snap)s', @@ -410,7 +435,8 @@ class QnapShareDriver(driver.ShareDriver): @utils.retry(exception=exception.ShareBackendException, interval=3, - retries=200) + retries=5) + @utils.synchronized('qnap-create_share_from_snapshot') def create_share_from_snapshot(self, context, share, snapshot, share_server=None): """Create a share from a snapshot.""" @@ -441,20 +467,24 @@ class QnapShareDriver(driver.ShareDriver): created_share = self.api_executor.get_share_info( self.configuration.qnap_poolname, vol_label=create_share_name) - if created_share.find('vol_no') is not None: + if created_share is not None: create_volID = created_share.find('vol_no').text + LOG.debug('create_volID: %s', create_volID) else: msg = _("Failed to clone a snapshot in time.") raise exception.ShareBackendException(msg=msg) - snap_share = self.share_api.get(context, - snapshot['share_instance']['share_id']) + snap_share = self.share_api.get( + context, snapshot['share_instance']['share_id']) LOG.debug('snap_share[size]: %s', snap_share['size']) if (share['size'] > snap_share['size']): - share_dict = {'sharename': create_share_name, - 'old_sharename': create_share_name, - 'new_size': share['size']} + share_dict = { + 'sharename': create_share_name, + 'old_sharename': create_share_name, + 'new_size': share['size'], + 'share_proto': share['share_proto'] + } self.api_executor.edit_share(share_dict) # Use private_storage to record volume ID and Name created in the NAS. @@ -470,7 +500,8 @@ class QnapShareDriver(driver.ShareDriver): return self._get_location_path(create_share_name, share['share_proto'], - self.configuration.qnap_share_ip) + self.configuration.qnap_share_ip, + create_volID) def _get_manila_hostIPv4s(self, hostlist): host_dict_IPs = [] @@ -656,7 +687,8 @@ class QnapShareDriver(driver.ShareDriver): export_locations = self._get_location_path( share_name, share['share_proto'], - self.configuration.qnap_share_ip) + self.configuration.qnap_share_ip, + vol_no) return {'size': vol_size_gb, 'export_locations': export_locations} diff --git a/manila/tests/share/drivers/qnap/fakes.py b/manila/tests/share/drivers/qnap/fakes.py index bd735e21bf..22b514d1f0 100644 --- a/manila/tests/share/drivers/qnap/fakes.py +++ b/manila/tests/share/drivers/qnap/fakes.py @@ -19,7 +19,18 @@ FAKE_RES_DETAIL_DATA_LOGIN = """ """ -FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES = """ +FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES_1_1_1 = """ + + + + + + + + + """ + +FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES_1_1_3 = """ @@ -30,7 +41,18 @@ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES = """ """ -FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS = """ +FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS_4_0_0 = """ + + + + + + + + + """ + +FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS_4_3_0 = """ @@ -41,7 +63,18 @@ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS = """ """ -FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS = """ +FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS_4_0_0 = """ + + + + + + + + + """ + +FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS_4_3_0 = """ @@ -52,7 +85,18 @@ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS = """ """ -FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES = """ +FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES_1_1_1 = """ + + + + + + + + + """ + +FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES_1_1_3 = """ @@ -208,6 +252,20 @@ FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT = """ 0 """ +FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT_SNAPSHOT_NOT_EXIST = """ + + + + -206021 + """ + +FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT_SHARE_NOT_EXIST = """ + + + + -200005 + """ + FAKE_RES_DETAIL_DATA_GET_HOST_LIST_API = """ @@ -347,44 +405,84 @@ class AccessClass(object): }[arg] -class FakeGetBasicInfoResponseEs(object): +class FakeGetBasicInfoResponseEs_1_1_1(object): """Fake GetBasicInfo response from ES nas.""" status = 'fackStatus' def read(self): """Mock response.read.""" - return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES + return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES_1_1_1 -class FakeGetBasicInfoResponseTs(object): +class FakeGetBasicInfoResponseEs_1_1_3(object): + """Fake GetBasicInfo response from ES nas.""" + + status = 'fackStatus' + + def read(self): + """Mock response.read.""" + return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES_1_1_3 + + +class FakeGetBasicInfoResponseTs_4_0_0(object): """Fake GetBasicInfoTS response from TS nas.""" status = 'fackStatus' def read(self): """Mock response.read.""" - return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS + return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS_4_0_0 -class FakeGetBasicInfoResponseTesTs(object): +class FakeGetBasicInfoResponseTs_4_3_0(object): """Fake GetBasicInfoTS response from TS nas.""" status = 'fackStatus' def read(self): """Mock response.read.""" - return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS + return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS_4_3_0 -class FakeGetBasicInfoResponseTesEs(object): +class FakeGetBasicInfoResponseTesTs_4_0_0(object): """Fake GetBasicInfoTS response from TS nas.""" status = 'fackStatus' def read(self): """Mock response.read.""" - return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES + return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS_4_0_0 + + +class FakeGetBasicInfoResponseTesTs_4_3_0(object): + """Fake GetBasicInfoTS response from TS nas.""" + + status = 'fackStatus' + + def read(self): + """Mock response.read.""" + return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS_4_3_0 + + +class FakeGetBasicInfoResponseTesEs_1_1_1(object): + """Fake GetBasicInfoTS response from TS nas.""" + + status = 'fackStatus' + + def read(self): + """Mock response.read.""" + return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES_1_1_1 + + +class FakeGetBasicInfoResponseTesEs_1_1_3(object): + """Fake GetBasicInfoTS response from TS nas.""" + + status = 'fackStatus' + + def read(self): + """Mock response.read.""" + return FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES_1_1_3 class FakeGetBasicInfoResponseError(object): @@ -418,7 +516,7 @@ class FakeDeleteShareResponse(object): class FakeDeleteSnapshotResponse(object): - """Fake pool info response.""" + """Fake delete snapshot response.""" status = 'fackStatus' @@ -427,6 +525,26 @@ class FakeDeleteSnapshotResponse(object): return FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT +class FakeDeleteSnapshotResponseSnapshotNotExist(object): + """Fake delete snapshot response.""" + + status = 'fackStatus' + + def read(self): + """Mock response.read.""" + return FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT_SNAPSHOT_NOT_EXIST + + +class FakeDeleteSnapshotResponseShareNotExist(object): + """Fake delete snapshot response.""" + + status = 'fackStatus' + + def read(self): + """Mock response.read.""" + return FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT_SHARE_NOT_EXIST + + class FakeGetHostListResponse(object): """Fake pool info response.""" diff --git a/manila/tests/share/drivers/qnap/test_api.py b/manila/tests/share/drivers/qnap/test_api.py index da46734748..693c7e038c 100644 --- a/manila/tests/share/drivers/qnap/test_api.py +++ b/manila/tests/share/drivers/qnap/test_api.py @@ -19,6 +19,7 @@ import ddt import mock import six from six.moves import urllib +import time from manila import exception from manila.share.drivers.qnap import qnap @@ -106,7 +107,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeCreateShareResponse()] @@ -164,7 +165,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeDeleteShareResponse()] @@ -198,7 +199,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeSpecificPoolInfoResponse()] @@ -235,7 +236,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeShareInfoResponse()] @@ -268,7 +269,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeSpecificVolInfoResponse()] @@ -302,7 +303,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeSnapshotInfoResponse()] @@ -337,7 +338,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeCreateSnapshotResponse()] @@ -368,14 +369,17 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): expected_call_list, mock_http_connection.return_value.request.call_args_list) - def test_delete_snapshot_api(self): + @ddt.data(fakes.FakeDeleteSnapshotResponse(), + fakes.FakeDeleteSnapshotResponseSnapshotNotExist(), + fakes.FakeDeleteSnapshotResponseShareNotExist()) + def test_delete_snapshot_api(self, fakeDeleteSnapshotResponse): """Test delete snapshot api.""" mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), - fakes.FakeDeleteSnapshotResponse()] + fakeDeleteSnapshotResponse] self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1') @@ -405,7 +409,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeDeleteSnapshotResponse()] @@ -440,7 +444,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseTs(), + fakes.FakeGetBasicInfoResponseTs_4_3_0(), fakes.FakeLoginResponse(), fakes.FakeCreateSnapshotResponse()] @@ -450,6 +454,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): "sharename": 'fakeVolId', "old_sharename": 'fakeVolId', "new_size": 100, + "share_proto": "NFS" } self.driver.api_executor.edit_share( expect_share_dict) @@ -464,8 +469,10 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): 'compression': '1', 'thin_pro': '0', 'cache': '0', + 'cifs_enable': '0', + 'nfs_enable': '1', 'afp_enable': '0', - 'ftp_enable': '1', + 'ftp_enable': '0', 'hidden': '0', 'oplocks': '1', 'sync': 'always', @@ -491,7 +498,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeGetHostListResponse()] @@ -523,7 +530,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeGetHostListResponse()] @@ -558,7 +565,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fakes.FakeGetHostListResponse()] @@ -594,7 +601,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseTs(), + fakes.FakeGetBasicInfoResponseTs_4_3_0(), fakes.FakeLoginResponse(), fakes.FakeSnapshotInfoResponse()] @@ -633,14 +640,20 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection.return_value.getresponse.side_effect = [ fakes.FakeLoginResponse(), - fakes.FakeGetBasicInfoResponseEs(), + fakes.FakeGetBasicInfoResponseEs_1_1_3(), fakes.FakeLoginResponse(), fake_fail_response, fake_fail_response, fake_fail_response, fake_fail_response, + fake_fail_response, + fake_fail_response, + fake_fail_response, + fake_fail_response, + fake_fail_response, fake_fail_response] + self.mock_object(time, 'sleep') self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1') self.assertRaises( @@ -655,115 +668,117 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): @ddt.data(['self.driver.api_executor.get_share_info', {'pool_id': 'fakeId'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.get_specific_volinfo', {'vol_id': 'fakeId'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.create_snapshot_api', {'volumeID': 'fakeVolumeId', 'snapshot_name': 'fakeSnapshotName'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.create_snapshot_api', {'volumeID': 'fakeVolumeId', 'snapshot_name': 'fakeSnapshotName'}, fakes.FakeEsResCodeNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.get_snapshot_info', {'volID': 'volId'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.get_snapshot_info', {'volID': 'volId'}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.get_specific_poolinfo', {'pool_id': 'Storage Pool 1'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.get_specific_poolinfo', {'pool_id': 'Storage Pool 1'}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.delete_share', {'vol_id': 'fakeId'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.delete_share', {'vol_id': 'fakeId'}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.delete_snapshot_api', {'snapshot_id': 'fakeSnapshotId'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.delete_snapshot_api', {'snapshot_id': 'fakeSnapshotId'}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.clone_snapshot', {'snapshot_id': 'fakeSnapshotId', 'new_sharename': 'fakeNewShareName'}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.clone_snapshot', {'snapshot_id': 'fakeSnapshotId', 'new_sharename': 'fakeNewShareName'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.edit_share', {'share_dict': {"sharename": 'fakeVolId', "old_sharename": 'fakeVolId', - "new_size": 100}}, + "new_size": 100, + "share_proto": "NFS"}}, fakes.FakeEsResCodeNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.edit_share', {'share_dict': {"sharename": 'fakeVolId', "old_sharename": 'fakeVolId', - "new_size": 100}}, + "new_size": 100, + "share_proto": "NFS"}}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.add_host', {'hostname': 'fakeHostName', 'ipv4': 'fakeIpV4'}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.add_host', {'hostname': 'fakeHostName', 'ipv4': 'fakeIpV4'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.get_host_list', {}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.get_host_list', {}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.set_nfs_access', {'sharename': 'fakeShareName', 'access': 'fakeAccess', 'host_name': 'fakeHostName'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.set_nfs_access', {'sharename': 'fakeShareName', 'access': 'fakeAccess', 'host_name': 'fakeHostName'}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseEs()], + fakes.FakeGetBasicInfoResponseEs_1_1_3()], ['self.driver.api_executor.get_snapshot_info', {'snapshot_name': 'fakeSnapshoName', 'lun_index': 'fakeLunIndex'}, fakes.FakeAuthPassFailResponse(), - fakes.FakeGetBasicInfoResponseTs()], + fakes.FakeGetBasicInfoResponseTs_4_3_0()], ['self.driver.api_executor.get_snapshot_info', {'snapshot_name': 'fakeSnapshoName', 'lun_index': 'fakeLunIndex'}, fakes.FakeResultNegativeResponse(), - fakes.FakeGetBasicInfoResponseTs()]) + fakes.FakeGetBasicInfoResponseTs_4_3_0()]) def test_get_snapshot_info_ts_with_fail_response( self, api, dict_parm, fake_fail_response, fake_basic_info): @@ -777,11 +792,16 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase): fake_fail_response, fake_fail_response, fake_fail_response, + fake_fail_response, + fake_fail_response, + fake_fail_response, + fake_fail_response, + fake_fail_response, fake_fail_response] self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1') - + self.mock_object(time, 'sleep') self.assertRaises( exception.ShareBackendException, eval(api), diff --git a/manila/tests/share/drivers/qnap/test_qnap.py b/manila/tests/share/drivers/qnap/test_qnap.py index 4fa06dcd2d..41b81f3da5 100644 --- a/manila/tests/share/drivers/qnap/test_qnap.py +++ b/manila/tests/share/drivers/qnap/test_qnap.py @@ -23,8 +23,11 @@ import ddt import mock from oslo_config import cfg import six +import time +from eventlet import greenthread from manila import exception +from manila.share.drivers.qnap import api from manila.share.drivers.qnap import qnap from manila import test from manila.tests import fake_share @@ -86,7 +89,8 @@ class QnapShareDriverLoginTestCase(QnapShareDriverBaseTestCase): def test_do_setup_positive(self, mng_url, port, ssl): """Test do_setup with http://1.2.3.4:8080.""" fake_login_response = fakes.FakeLoginResponse() - fake_get_basic_info_response_es = fakes.FakeGetBasicInfoResponseEs() + fake_get_basic_info_response_es = ( + fakes.FakeGetBasicInfoResponseEs_1_1_3()) if ssl: mock_connection = six.moves.http_client.HTTPSConnection else: @@ -117,13 +121,12 @@ class QnapShareDriverLoginTestCase(QnapShareDriverBaseTestCase): self.assertEqual(port, self.driver.api_executor.port) self.assertEqual(ssl, self.driver.api_executor.ssl) - @ddt.data(fakes.FakeGetBasicInfoResponseTs(), - fakes.FakeGetBasicInfoResponseTesTs(), - fakes.FakeGetBasicInfoResponseTesEs()) + @ddt.data(fakes.FakeGetBasicInfoResponseTs_4_3_0(), + fakes.FakeGetBasicInfoResponseTesTs_4_3_0(), + fakes.FakeGetBasicInfoResponseTesEs_1_1_3()) def test_do_setup_positive_with_diff_nas(self, fake_basic_info): """Test do_setup with different NAS model.""" fake_login_response = fakes.FakeLoginResponse() - # fake_get_basic_info_response_ts = FakeGetBasicInfoResponseTs() mock_connection = six.moves.http_client.HTTPSConnection mock_connection.return_value.getresponse.side_effect = [ fake_login_response, @@ -140,6 +143,64 @@ class QnapShareDriverLoginTestCase(QnapShareDriverBaseTestCase): self.assertEqual('443', self.driver.api_executor.port) self.assertTrue(self.driver.api_executor.ssl) + @ddt.data({ + 'fake_basic_info': fakes.FakeGetBasicInfoResponseTs_4_3_0(), + 'expect_result': api.QnapAPIExecutorTS + }, { + 'fake_basic_info': fakes.FakeGetBasicInfoResponseTesTs_4_3_0(), + 'expect_result': api.QnapAPIExecutorTS + }, { + 'fake_basic_info': fakes.FakeGetBasicInfoResponseTesEs_1_1_3(), + 'expect_result': api.QnapAPIExecutor + }, { + 'fake_basic_info': fakes.FakeGetBasicInfoResponseEs_1_1_3(), + 'expect_result': api.QnapAPIExecutor + }) + @ddt.unpack + def test_create_api_executor(self, fake_basic_info, expect_result): + """Test do_setup with different NAS model.""" + fake_login_response = fakes.FakeLoginResponse() + mock_connection = six.moves.http_client.HTTPSConnection + mock_connection.return_value.getresponse.side_effect = [ + fake_login_response, + fake_basic_info, + fake_login_response] + self._do_setup('https://1.2.3.4:443', '1.2.3.4', 'admin', + 'qnapadmin', 'Storage Pool 1') + self.assertIsInstance(self.driver.api_executor, expect_result) + + @ddt.data({ + 'fake_basic_info': fakes.FakeGetBasicInfoResponseTs_4_0_0(), + 'expect_result': exception.ShareBackendException + }, { + 'fake_basic_info': fakes.FakeGetBasicInfoResponseTesTs_4_0_0(), + 'expect_result': exception.ShareBackendException + }, { + 'fake_basic_info': fakes.FakeGetBasicInfoResponseTesEs_1_1_1(), + 'expect_result': exception.ShareBackendException + }, { + 'fake_basic_info': fakes.FakeGetBasicInfoResponseEs_1_1_1(), + 'expect_result': exception.ShareBackendException + }) + @ddt.unpack + def test_create_api_executor_negative(self, + fake_basic_info, expect_result): + """Test do_setup with different NAS model.""" + fake_login_response = fakes.FakeLoginResponse() + mock_connection = six.moves.http_client.HTTPSConnection + mock_connection.return_value.getresponse.side_effect = [ + fake_login_response, + fake_basic_info, + fake_login_response] + self.assertRaises( + exception.ShareBackendException, + self._do_setup, + 'https://1.2.3.4:443', + '1.2.3.4', + 'admin', + 'qnapadmin', + 'Storage Pool 1') + def test_do_setup_with_exception(self): """Test do_setup with exception.""" fake_login_response = fakes.FakeLoginResponse() @@ -243,28 +304,81 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): mock_get_location_path): """Test create share.""" mock_api_executor = qnap.QnapShareDriver._create_api_executor - mock_api_executor.return_value.get_share_info.return_value = None + 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_api_executor.return_value.create_share.return_value = ( 'fakeCreateShareId') mock_get_location_path.return_value = None mock_private_storage = mock.Mock() - + self.mock_object(greenthread, 'sleep') self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1', private_storage=mock_private_storage) self.driver.create_share('context', self.share) - mock_api_executor.return_value.get_share_info.assert_called_once_with( - 'Storage Pool 1', - vol_label='fakeShareName') + mock_api_return = mock_api_executor.return_value + expected_call_list = [ + mock.call('Storage Pool 1', vol_label='fakeShareName'), + mock.call('Storage Pool 1', vol_label='fakeShareName')] + self.assertEqual( + expected_call_list, + mock_api_return.get_share_info.call_args_list) mock_api_executor.return_value.create_share.assert_called_once_with( self.share, self.driver.configuration.qnap_poolname, 'fakeShareName', 'NFS') mock_get_location_path.assert_called_once_with( - 'fakeShareName', 'NFS', '1.2.3.4') + 'fakeShareName', 'NFS', '1.2.3.4', 'fakeNo') + + @mock.patch.object(qnap.QnapShareDriver, '_get_location_path') + @mock.patch.object(qnap.QnapShareDriver, '_gen_random_name') + def test_create_share_negative_share_exist( + 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.return_value = ( + 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.mock_object(time, 'sleep') + self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', + 'qnapadmin', 'Storage Pool 1', + private_storage=mock_private_storage) + + self.assertRaises( + exception.ShareBackendException, + self.driver.create_share, + 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_create_fail( + 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.return_value = None + mock_gen_random_name.return_value = 'fakeShareName' + mock_get_location_path.return_value = None + mock_private_storage = mock.Mock() + self.mock_object(time, 'sleep') + self.mock_object(greenthread, 'sleep') + self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', + 'qnapadmin', 'Storage Pool 1', + private_storage=mock_private_storage) + + self.assertRaises( + exception.ShareBackendException, + self.driver.create_share, + context='context', + share=self.share) def test_delete_share_positive(self): """Test delete share with fake_share.""" @@ -323,7 +437,7 @@ 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 = 'fakeVolId' + mock_private_storage.get.return_value = 'fakeVolName' self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', 'qnapadmin', 'Storage Pool 1', @@ -331,9 +445,10 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): self.driver.extend_share(self.share, 100, share_server=None) expect_share_dict = { - "sharename": 'fakeVolId', - "old_sharename": 'fakeVolId', - "new_size": 100, + 'sharename': 'fakeVolName', + 'old_sharename': 'fakeVolName', + 'new_size': 100, + 'share_proto': 'NFS' } mock_api_executor.return_value.edit_share.assert_called_once_with( expect_share_dict) @@ -455,10 +570,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): mock_api_executor = qnap.QnapShareDriver._create_api_executor mock_gen_random_name.return_value = 'fakeShareName' - mock_api_executor.return_value.get_share_info.side_effect = ( - None, self.get_share_info_return_value()) - mock_api_executor.return_value.clone_snapshot.return_value = ( - None) + 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 = 'fakeSnapshotId' mock_share_api.return_value.get.return_value = {'size': 10} @@ -495,12 +608,10 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): mock_gen_random_name.return_value = 'fakeShareName' 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_api_executor.return_value.clone_snapshot.return_value = ( - None) + 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 = 'fakeSnapshotId' + mock_private_storage.get.return_value = 'fakeVolName' mock_share_api.return_value.get.return_value = {'size': 5} mock_api_executor.return_value.edit_share.return_value = ( None) @@ -525,7 +636,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): expect_share_dict = { 'sharename': 'fakeShareName', 'old_sharename': 'fakeShareName', - 'new_size': 10 + 'new_size': 10, + 'share_proto': 'NFS' } mock_api_return.edit_share.assert_called_once_with( expect_share_dict) @@ -548,6 +660,67 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): snapshot=fake_snapshot, share_server=None) + @mock.patch.object(qnap.QnapShareDriver, '_get_location_path') + @mock.patch('manila.share.API') + @mock.patch.object(qnap.QnapShareDriver, '_gen_random_name') + def test_create_share_from_snapshot_negative_name_exist( + self, + mock_gen_random_name, + mock_share_api, + mock_get_location_path): + """Test create share from snapshot.""" + fake_snapshot = fakes.SnapshotClass( + 10, 'fakeShareName@fakeSnapshotName') + + mock_api_executor = qnap.QnapShareDriver._create_api_executor + mock_gen_random_name.return_value = 'fakeShareName' + mock_api_executor.return_value.get_share_info.return_value = ( + self.get_share_info_return_value()) + mock_private_storage = mock.Mock() + mock_private_storage.get.return_value = 'fakeSnapshotId' + mock_share_api.return_value.get.return_value = {'size': 10} + self.mock_object(time, 'sleep') + self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', + 'qnapadmin', 'Storage Pool 1', + private_storage=mock_private_storage) + self.assertRaises( + exception.ShareBackendException, + self.driver.create_share_from_snapshot, + context='context', + share=self.share, + snapshot=fake_snapshot, + share_server=None) + + @mock.patch.object(qnap.QnapShareDriver, '_get_location_path') + @mock.patch('manila.share.API') + @mock.patch.object(qnap.QnapShareDriver, '_gen_random_name') + def test_create_share_from_snapshot_negative_clone_fail( + self, + mock_gen_random_name, + mock_share_api, + mock_get_location_path): + """Test create share from snapshot.""" + fake_snapshot = fakes.SnapshotClass( + 10, 'fakeShareName@fakeSnapshotName') + + mock_api_executor = qnap.QnapShareDriver._create_api_executor + mock_gen_random_name.return_value = 'fakeShareName' + mock_api_executor.return_value.get_share_info.return_value = None + mock_private_storage = mock.Mock() + mock_private_storage.get.return_value = 'fakeSnapshotId' + mock_share_api.return_value.get.return_value = {'size': 10} + self.mock_object(time, 'sleep') + self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', + 'qnapadmin', 'Storage Pool 1', + private_storage=mock_private_storage) + self.assertRaises( + exception.ShareBackendException, + self.driver.create_share_from_snapshot, + context='context', + share=self.share, + snapshot=fake_snapshot, + share_server=None) + @mock.patch.object(qnap.QnapShareDriver, '_allow_access') def test_update_access_allow_access( self, mock_allow_access): @@ -644,7 +817,28 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): mock_api_return.get_specific_volinfo.assert_called_once_with( 'fakeNo') mock_get_location_path.assert_called_once_with( - 'fakeShareName', 'NFS', '1.2.3.4') + 'fakeShareName', 'NFS', '1.2.3.4', 'fakeNo') + + def test_manage_invalid_protocol(self): + """Test manage existing.""" + share = fake_share.fake_share( + share_proto='fakeProtocol', + id='fakeId', + display_name='fakeDisplayName', + export_locations=[{'path': ''}], + host='QnapShareDriver', + size=10) + + 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.assertRaises( + exception.InvalidInput, + self.driver.manage_existing, + share=share, + driver_options='driver_options') def test_manage_existing_nfs_without_export_locations(self): share = fake_share.fake_share( @@ -820,14 +1014,15 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase): } self.assertEqual( expect_result, self.driver._get_location_path( - 'fakeShareName', 'NFS', 'fakeIp')) + 'fakeShareName', 'NFS', 'fakeIp', 'fakeVolId')) self.assertRaises( exception.InvalidInput, self.driver._get_location_path, share_name='fakeShareName', share_proto='fakeProto', - ip='fakeIp') + ip='fakeIp', + vol_id='fakeVolId') def test_update_share_stats(self): """Test update share stats.""" diff --git a/releasenotes/notes/support-qes-114-5881c0ff0e7da512.yaml b/releasenotes/notes/support-qes-114-5881c0ff0e7da512.yaml new file mode 100644 index 0000000000..27039f74b3 --- /dev/null +++ b/releasenotes/notes/support-qes-114-5881c0ff0e7da512.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + QNAP Manila driver adds support for QES fw 1.1.4.