QNAP: Add support for QES 1.1.4

This patch do the following changes to the QNAP Manila driver:
1. Add support for QNAP QES firmware 1.1.4.
2. Correct the default parameters in edit share function.
2. Remove unused code flow.
4. Add more debug log and write more test cases.

    Implements: blueprint support-qes-114

Change-Id: I977ef3e66a21a52116b44059ad5aeaf1560b47b7
This commit is contained in:
Chris Yang 2017-11-27 12:43:25 +08:00
parent 3088bf8ebf
commit 4c2b5e9335
6 changed files with 515 additions and 145 deletions

View File

@ -39,10 +39,10 @@ MSG_SESSION_EXPIRED = _("Session ID expired")
MSG_UNEXPECT_RESP = _("Unexpected response from QNAP API") MSG_UNEXPECT_RESP = _("Unexpected response from QNAP API")
@utils.retry(exception=exception.ShareBackendException,
retries=5)
def _connection_checker(func): def _connection_checker(func):
"""Decorator to check session has expired or not.""" """Decorator to check session has expired or not."""
@utils.retry(exception=exception.ShareBackendException,
retries=5)
@functools.wraps(func) @functools.wraps(func)
def inner_connection_checker(self, *args, **kwargs): def inner_connection_checker(self, *args, **kwargs):
LOG.debug('in _connection_checker') LOG.debug('in _connection_checker')
@ -225,7 +225,8 @@ class QnapAPIExecutor(object):
if root.find('authPassed').text == '0': if root.find('authPassed').text == '0':
raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED) raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED)
if root.find('ES_RET_CODE').text < '0': 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) raise exception.ShareBackendException(msg=msg)
vol_list = root.find('func').find('ownContent').find('volumeList') vol_list = root.find('func').find('ownContent').find('volumeList')
@ -319,26 +320,22 @@ class QnapAPIExecutor(object):
if root.find('authPassed').text == '0': if root.find('authPassed').text == '0':
raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED) raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED)
if ('vol_no' in kwargs) or ('vol_label' in kwargs): vol_list = root.find('Volume_Info')
vol_list = root.find('Volume_Info') vol_info_tree = vol_list.findall('row')
vol_info_tree = vol_list.findall('row') for vol in vol_info_tree:
for vol in vol_info_tree: LOG.debug('Iterating vol name: %(name)s, index: %(id)s',
LOG.debug('Iterating vol name: %(name)s, index: %(id)s', {'name': vol.find('vol_label').text,
{'name': vol.find('vol_label').text, 'id': vol.find('vol_no').text})
'id': vol.find('vol_no').text}) if 'vol_no' in kwargs:
if 'vol_no' in kwargs: if kwargs['vol_no'] == vol.find('vol_no').text:
if kwargs['vol_no'] == vol.find('vol_no').text: LOG.debug('vol_no:%s',
LOG.debug('vol_no:%s', vol.find('vol_no').text)
vol.find('vol_no').text) return vol
return vol elif 'vol_label' in kwargs:
elif 'vol_label' in kwargs: if kwargs['vol_label'] == vol.find('vol_label').text:
if kwargs['vol_label'] == vol.find('vol_label').text: LOG.debug('vol_label:%s', vol.find('vol_label').text)
LOG.debug('vol_label:%s', vol.find('vol_label').text) return vol
return vol return None
if vol is vol_info_tree[-1]:
return None
else:
return res_details['data']
@_connection_checker @_connection_checker
def get_specific_volinfo(self, vol_id, **kwargs): def get_specific_volinfo(self, vol_id, **kwargs):
@ -453,9 +450,11 @@ class QnapAPIExecutor(object):
raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED) raise exception.ShareBackendException(msg=MSG_SESSION_EXPIRED)
# snapshot not exist # snapshot not exist
if root.find('result').text == '-206021': if root.find('result').text == '-206021':
LOG.warning('Snapshot id %s does not exist', snapshot_id)
return return
# lun not exist # share not exist
if root.find('result').text == '-200005': if root.find('result').text == '-200005':
LOG.warning('Share of snapshot id %s does not exist', snapshot_id)
return return
if root.find('result').text < '0': if root.find('result').text < '0':
msg = _('Failed to delete snapshot.') msg = _('Failed to delete snapshot.')
@ -499,8 +498,10 @@ class QnapAPIExecutor(object):
'compression': '1', 'compression': '1',
'thin_pro': '0', 'thin_pro': '0',
'cache': '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', 'afp_enable': '0',
'ftp_enable': '1', 'ftp_enable': '0',
'hidden': '0', 'hidden': '0',
'oplocks': '1', 'oplocks': '1',
'sync': 'always', 'sync': 'always',

View File

@ -61,9 +61,10 @@ class QnapShareDriver(driver.ShareDriver):
Version history: Version history:
1.0.0 - Initial driver (Only NFS) 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): def __init__(self, *args, **kwargs):
"""Initialize QnapShareDriver.""" """Initialize QnapShareDriver."""
@ -152,17 +153,14 @@ class QnapShareDriver(driver.ShareDriver):
username=self.configuration.qnap_nas_login, username=self.configuration.qnap_nas_login,
password=self.configuration.qnap_nas_password, password=self.configuration.qnap_nas_password,
management_url=self.configuration.qnap_management_url) management_url=self.configuration.qnap_management_url)
elif "1.1.2" <= fw_version <= "1.1.4":
if (fw_version.startswith("1.1.2") or
fw_version.startswith("1.1.3")):
LOG.debug('Create ES API Executor') LOG.debug('Create ES API Executor')
return api.QnapAPIExecutor( return api.QnapAPIExecutor(
username=self.configuration.qnap_nas_login, username=self.configuration.qnap_nas_login,
password=self.configuration.qnap_nas_password, password=self.configuration.qnap_nas_password,
management_url=self.configuration.qnap_management_url) management_url=self.configuration.qnap_management_url)
elif model_type in es_model_types: elif model_type in es_model_types:
if (fw_version.startswith("1.1.2") or if "1.1.2" <= fw_version <= "1.1.4":
fw_version.startswith("1.1.3")):
LOG.debug('Create ES API Executor') LOG.debug('Create ES API Executor')
return api.QnapAPIExecutor( return api.QnapAPIExecutor(
username=self.configuration.qnap_nas_login, username=self.configuration.qnap_nas_login,
@ -195,13 +193,9 @@ class QnapShareDriver(driver.ShareDriver):
{'ifx': infix, {'ifx': infix,
'time': timeutils.utcnow().strftime('%Y%m%d%H%M%S%f')}) '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': if share_proto == 'NFS':
created_share = self.api_executor.get_share_info( vol = self.api_executor.get_specific_volinfo(vol_id)
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_mount_path = vol.find('vol_mount_path').text vol_mount_path = vol.find('vol_mount_path').text
location = '%s:%s' % (ip, vol_mount_path) location = '%s:%s' % (ip, vol_mount_path)
@ -263,7 +257,8 @@ class QnapShareDriver(driver.ShareDriver):
@utils.retry(exception=exception.ShareBackendException, @utils.retry(exception=exception.ShareBackendException,
interval=3, interval=3,
retries=200) retries=5)
@utils.synchronized('qnap-create_share')
def create_share(self, context, share, share_server=None): def create_share(self, context, share, share_server=None):
"""Create a new share.""" """Create a new share."""
LOG.debug('share: %s', share.__dict__) LOG.debug('share: %s', share.__dict__)
@ -277,25 +272,48 @@ class QnapShareDriver(driver.ShareDriver):
created_share = self.api_executor.get_share_info( created_share = self.api_executor.get_share_info(
self.configuration.qnap_poolname, self.configuration.qnap_poolname,
vol_label=create_share_name) vol_label=create_share_name)
LOG.debug('created_share: %s', created_share)
if created_share is not None: 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) raise exception.ShareBackendException(msg=msg)
create_volID = self.api_executor.create_share( self.api_executor.create_share(
share, share,
self.configuration.qnap_poolname, self.configuration.qnap_poolname,
create_share_name, create_share_name,
share_proto) 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. # 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) self.private_storage.update(share['id'], _metadata)
return self._get_location_path(create_share_name, return self._get_location_path(create_share_name,
share['share_proto'], 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): def delete_share(self, context, share, share_server=None):
"""Delete the specified share.""" """Delete the specified share."""
# Use private_storage to retrieve volume ID created in the NAS. # 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.api_executor.delete_share(vol_no)
self.private_storage.delete(share['id']) self.private_storage.delete(share['id'])
@utils.synchronized('qnap-extend_share')
def extend_share(self, share, new_size, share_server=None): def extend_share(self, share, new_size, share_server=None):
"""Extend an existing share.""" """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', '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. # Use private_storage to retrieve volume Name created in the NAS.
volName = self.private_storage.get(share['id'], 'volName') volName = self.private_storage.get(share['id'], 'volName')
@ -331,15 +353,17 @@ class QnapShareDriver(driver.ShareDriver):
LOG.debug('volName: %s', volName) LOG.debug('volName: %s', volName)
share_dict = { share_dict = {
"sharename": volName, 'sharename': volName,
"old_sharename": volName, 'old_sharename': volName,
"new_size": new_size, 'new_size': new_size,
'share_proto': share['share_proto']
} }
self.api_executor.edit_share(share_dict) self.api_executor.edit_share(share_dict)
@utils.retry(exception=exception.ShareBackendException, @utils.retry(exception=exception.ShareBackendException,
interval=3, interval=3,
retries=200) retries=5)
@utils.synchronized('qnap-create_snapshot')
def create_snapshot(self, context, snapshot, share_server=None): def create_snapshot(self, context, snapshot, share_server=None):
"""Create a snapshot.""" """Create a snapshot."""
LOG.debug('snapshot[share][share_id]: %s', LOG.debug('snapshot[share][share_id]: %s',
@ -393,6 +417,7 @@ class QnapShareDriver(driver.ShareDriver):
return {'provider_location': snapshot_id} return {'provider_location': snapshot_id}
@utils.synchronized('qnap-delete_snapshot')
def delete_snapshot(self, context, snapshot, share_server=None): def delete_snapshot(self, context, snapshot, share_server=None):
"""Delete a snapshot.""" """Delete a snapshot."""
LOG.debug('Entering delete_snapshot. The deleted snapshot=%(snap)s', LOG.debug('Entering delete_snapshot. The deleted snapshot=%(snap)s',
@ -410,7 +435,8 @@ class QnapShareDriver(driver.ShareDriver):
@utils.retry(exception=exception.ShareBackendException, @utils.retry(exception=exception.ShareBackendException,
interval=3, interval=3,
retries=200) retries=5)
@utils.synchronized('qnap-create_share_from_snapshot')
def create_share_from_snapshot(self, context, share, snapshot, def create_share_from_snapshot(self, context, share, snapshot,
share_server=None): share_server=None):
"""Create a share from a snapshot.""" """Create a share from a snapshot."""
@ -441,20 +467,24 @@ class QnapShareDriver(driver.ShareDriver):
created_share = self.api_executor.get_share_info( created_share = self.api_executor.get_share_info(
self.configuration.qnap_poolname, self.configuration.qnap_poolname,
vol_label=create_share_name) 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 create_volID = created_share.find('vol_no').text
LOG.debug('create_volID: %s', create_volID)
else: else:
msg = _("Failed to clone a snapshot in time.") msg = _("Failed to clone a snapshot in time.")
raise exception.ShareBackendException(msg=msg) raise exception.ShareBackendException(msg=msg)
snap_share = self.share_api.get(context, snap_share = self.share_api.get(
snapshot['share_instance']['share_id']) context, snapshot['share_instance']['share_id'])
LOG.debug('snap_share[size]: %s', snap_share['size']) LOG.debug('snap_share[size]: %s', snap_share['size'])
if (share['size'] > snap_share['size']): if (share['size'] > snap_share['size']):
share_dict = {'sharename': create_share_name, share_dict = {
'old_sharename': create_share_name, 'sharename': create_share_name,
'new_size': share['size']} 'old_sharename': create_share_name,
'new_size': share['size'],
'share_proto': share['share_proto']
}
self.api_executor.edit_share(share_dict) self.api_executor.edit_share(share_dict)
# Use private_storage to record volume ID and Name created in the NAS. # 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, return self._get_location_path(create_share_name,
share['share_proto'], share['share_proto'],
self.configuration.qnap_share_ip) self.configuration.qnap_share_ip,
create_volID)
def _get_manila_hostIPv4s(self, hostlist): def _get_manila_hostIPv4s(self, hostlist):
host_dict_IPs = [] host_dict_IPs = []
@ -656,7 +687,8 @@ class QnapShareDriver(driver.ShareDriver):
export_locations = self._get_location_path( export_locations = self._get_location_path(
share_name, share_name,
share['share_proto'], share['share_proto'],
self.configuration.qnap_share_ip) self.configuration.qnap_share_ip,
vol_no)
return {'size': vol_size_gb, 'export_locations': export_locations} return {'size': vol_size_gb, 'export_locations': export_locations}

View File

@ -19,7 +19,18 @@ FAKE_RES_DETAIL_DATA_LOGIN = """
<authSid><![CDATA[fakeSid]]></authSid> <authSid><![CDATA[fakeSid]]></authSid>
</QDocRoot>""" </QDocRoot>"""
FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES = """ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES_1_1_1 = """
<QDocRoot version="1.0">
<model>
<displayModelName><![CDATA[ES1640dc]]></displayModelName>
<internalModelName><![CDATA[ES1640dc]]></internalModelName>
</model>
<firmware>
<version><![CDATA[1.1.1]]></version>
</firmware>
</QDocRoot>"""
FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES_1_1_3 = """
<QDocRoot version="1.0"> <QDocRoot version="1.0">
<model> <model>
<displayModelName><![CDATA[ES1640dc]]></displayModelName> <displayModelName><![CDATA[ES1640dc]]></displayModelName>
@ -30,7 +41,18 @@ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_ES = """
</firmware> </firmware>
</QDocRoot>""" </QDocRoot>"""
FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS = """ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS_4_0_0 = """
<QDocRoot version="1.0">
<model>
<displayModelName><![CDATA[TS-870U]]></displayModelName>
<internalModelName><![CDATA[TS-870U]]></internalModelName>
</model>
<firmware>
<version><![CDATA[4.0.0]]></version>
</firmware>
</QDocRoot>"""
FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS_4_3_0 = """
<QDocRoot version="1.0"> <QDocRoot version="1.0">
<model> <model>
<displayModelName><![CDATA[TS-870U]]></displayModelName> <displayModelName><![CDATA[TS-870U]]></displayModelName>
@ -41,7 +63,18 @@ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TS = """
</firmware> </firmware>
</QDocRoot>""" </QDocRoot>"""
FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS = """ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS_4_0_0 = """
<QDocRoot version="1.0">
<model>
<displayModelName><![CDATA[TES-1885U]]></displayModelName>
<internalModelName><![CDATA[TS-1885U]]></internalModelName>
</model>
<firmware>
<version><![CDATA[4.0.0]]></version>
</firmware>
</QDocRoot>"""
FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS_4_3_0 = """
<QDocRoot version="1.0"> <QDocRoot version="1.0">
<model> <model>
<displayModelName><![CDATA[TES-1885U]]></displayModelName> <displayModelName><![CDATA[TES-1885U]]></displayModelName>
@ -52,7 +85,18 @@ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_TS = """
</firmware> </firmware>
</QDocRoot>""" </QDocRoot>"""
FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES = """ FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES_1_1_1 = """
<QDocRoot version="1.0">
<model>
<displayModelName><![CDATA[TES-1885U]]></displayModelName>
<internalModelName><![CDATA[ES-1885U]]></internalModelName>
</model>
<firmware>
<version><![CDATA[1.1.1]]></version>
</firmware>
</QDocRoot>"""
FAKE_RES_DETAIL_DATA_GETBASIC_INFO_TES_ES_1_1_3 = """
<QDocRoot version="1.0"> <QDocRoot version="1.0">
<model> <model>
<displayModelName><![CDATA[TES-1885U]]></displayModelName> <displayModelName><![CDATA[TES-1885U]]></displayModelName>
@ -208,6 +252,20 @@ FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT = """
<result>0</result> <result>0</result>
</QDocRoot>""" </QDocRoot>"""
FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT_SNAPSHOT_NOT_EXIST = """
<QDocRoot version="1.0">
<authPassed><![CDATA[1]]></authPassed>
<ES_RET_CODE><![CDATA[1]]></ES_RET_CODE>
<result>-206021</result>
</QDocRoot>"""
FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT_SHARE_NOT_EXIST = """
<QDocRoot version="1.0">
<authPassed><![CDATA[1]]></authPassed>
<ES_RET_CODE><![CDATA[1]]></ES_RET_CODE>
<result>-200005</result>
</QDocRoot>"""
FAKE_RES_DETAIL_DATA_GET_HOST_LIST_API = """ FAKE_RES_DETAIL_DATA_GET_HOST_LIST_API = """
<QDocRoot version="1.0"> <QDocRoot version="1.0">
<authPassed><![CDATA[1]]></authPassed> <authPassed><![CDATA[1]]></authPassed>
@ -347,44 +405,84 @@ class AccessClass(object):
}[arg] }[arg]
class FakeGetBasicInfoResponseEs(object): class FakeGetBasicInfoResponseEs_1_1_1(object):
"""Fake GetBasicInfo response from ES nas.""" """Fake GetBasicInfo response from ES nas."""
status = 'fackStatus' status = 'fackStatus'
def read(self): def read(self):
"""Mock response.read.""" """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.""" """Fake GetBasicInfoTS response from TS nas."""
status = 'fackStatus' status = 'fackStatus'
def read(self): def read(self):
"""Mock response.read.""" """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.""" """Fake GetBasicInfoTS response from TS nas."""
status = 'fackStatus' status = 'fackStatus'
def read(self): def read(self):
"""Mock response.read.""" """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.""" """Fake GetBasicInfoTS response from TS nas."""
status = 'fackStatus' status = 'fackStatus'
def read(self): def read(self):
"""Mock response.read.""" """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): class FakeGetBasicInfoResponseError(object):
@ -418,7 +516,7 @@ class FakeDeleteShareResponse(object):
class FakeDeleteSnapshotResponse(object): class FakeDeleteSnapshotResponse(object):
"""Fake pool info response.""" """Fake delete snapshot response."""
status = 'fackStatus' status = 'fackStatus'
@ -427,6 +525,26 @@ class FakeDeleteSnapshotResponse(object):
return FAKE_RES_DETAIL_DATA_DELETE_SNAPSHOT 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): class FakeGetHostListResponse(object):
"""Fake pool info response.""" """Fake pool info response."""

View File

@ -19,6 +19,7 @@ import ddt
import mock import mock
import six import six
from six.moves import urllib from six.moves import urllib
import time
from manila import exception from manila import exception
from manila.share.drivers.qnap import qnap 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 = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeCreateShareResponse()] fakes.FakeCreateShareResponse()]
@ -164,7 +165,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeDeleteShareResponse()] fakes.FakeDeleteShareResponse()]
@ -198,7 +199,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeSpecificPoolInfoResponse()] fakes.FakeSpecificPoolInfoResponse()]
@ -235,7 +236,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeShareInfoResponse()] fakes.FakeShareInfoResponse()]
@ -268,7 +269,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeSpecificVolInfoResponse()] fakes.FakeSpecificVolInfoResponse()]
@ -302,7 +303,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeSnapshotInfoResponse()] fakes.FakeSnapshotInfoResponse()]
@ -337,7 +338,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeCreateSnapshotResponse()] fakes.FakeCreateSnapshotResponse()]
@ -368,14 +369,17 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
expected_call_list, expected_call_list,
mock_http_connection.return_value.request.call_args_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.""" """Test delete snapshot api."""
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeDeleteSnapshotResponse()] fakeDeleteSnapshotResponse]
self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin',
'qnapadmin', 'Storage Pool 1') 'qnapadmin', 'Storage Pool 1')
@ -405,7 +409,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeDeleteSnapshotResponse()] fakes.FakeDeleteSnapshotResponse()]
@ -440,7 +444,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseTs(), fakes.FakeGetBasicInfoResponseTs_4_3_0(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeCreateSnapshotResponse()] fakes.FakeCreateSnapshotResponse()]
@ -450,6 +454,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
"sharename": 'fakeVolId', "sharename": 'fakeVolId',
"old_sharename": 'fakeVolId', "old_sharename": 'fakeVolId',
"new_size": 100, "new_size": 100,
"share_proto": "NFS"
} }
self.driver.api_executor.edit_share( self.driver.api_executor.edit_share(
expect_share_dict) expect_share_dict)
@ -464,8 +469,10 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
'compression': '1', 'compression': '1',
'thin_pro': '0', 'thin_pro': '0',
'cache': '0', 'cache': '0',
'cifs_enable': '0',
'nfs_enable': '1',
'afp_enable': '0', 'afp_enable': '0',
'ftp_enable': '1', 'ftp_enable': '0',
'hidden': '0', 'hidden': '0',
'oplocks': '1', 'oplocks': '1',
'sync': 'always', 'sync': 'always',
@ -491,7 +498,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetHostListResponse()] fakes.FakeGetHostListResponse()]
@ -523,7 +530,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetHostListResponse()] fakes.FakeGetHostListResponse()]
@ -558,7 +565,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetHostListResponse()] fakes.FakeGetHostListResponse()]
@ -594,7 +601,7 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseTs(), fakes.FakeGetBasicInfoResponseTs_4_3_0(),
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeSnapshotInfoResponse()] fakes.FakeSnapshotInfoResponse()]
@ -633,14 +640,20 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
mock_http_connection = six.moves.http_client.HTTPConnection mock_http_connection = six.moves.http_client.HTTPConnection
mock_http_connection.return_value.getresponse.side_effect = [ mock_http_connection.return_value.getresponse.side_effect = [
fakes.FakeLoginResponse(), fakes.FakeLoginResponse(),
fakes.FakeGetBasicInfoResponseEs(), fakes.FakeGetBasicInfoResponseEs_1_1_3(),
fakes.FakeLoginResponse(), 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,
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', self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin',
'qnapadmin', 'Storage Pool 1') 'qnapadmin', 'Storage Pool 1')
self.assertRaises( self.assertRaises(
@ -655,115 +668,117 @@ class QnapAPITestCase(QnapShareDriverBaseTestCase):
@ddt.data(['self.driver.api_executor.get_share_info', @ddt.data(['self.driver.api_executor.get_share_info',
{'pool_id': 'fakeId'}, {'pool_id': 'fakeId'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.get_specific_volinfo', ['self.driver.api_executor.get_specific_volinfo',
{'vol_id': 'fakeId'}, {'vol_id': 'fakeId'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.create_snapshot_api', ['self.driver.api_executor.create_snapshot_api',
{'volumeID': 'fakeVolumeId', {'volumeID': 'fakeVolumeId',
'snapshot_name': 'fakeSnapshotName'}, 'snapshot_name': 'fakeSnapshotName'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.create_snapshot_api', ['self.driver.api_executor.create_snapshot_api',
{'volumeID': 'fakeVolumeId', {'volumeID': 'fakeVolumeId',
'snapshot_name': 'fakeSnapshotName'}, 'snapshot_name': 'fakeSnapshotName'},
fakes.FakeEsResCodeNegativeResponse(), fakes.FakeEsResCodeNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.get_snapshot_info', ['self.driver.api_executor.get_snapshot_info',
{'volID': 'volId'}, {'volID': 'volId'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.get_snapshot_info', ['self.driver.api_executor.get_snapshot_info',
{'volID': 'volId'}, {'volID': 'volId'},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.get_specific_poolinfo', ['self.driver.api_executor.get_specific_poolinfo',
{'pool_id': 'Storage Pool 1'}, {'pool_id': 'Storage Pool 1'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.get_specific_poolinfo', ['self.driver.api_executor.get_specific_poolinfo',
{'pool_id': 'Storage Pool 1'}, {'pool_id': 'Storage Pool 1'},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.delete_share', ['self.driver.api_executor.delete_share',
{'vol_id': 'fakeId'}, {'vol_id': 'fakeId'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.delete_share', ['self.driver.api_executor.delete_share',
{'vol_id': 'fakeId'}, {'vol_id': 'fakeId'},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.delete_snapshot_api', ['self.driver.api_executor.delete_snapshot_api',
{'snapshot_id': 'fakeSnapshotId'}, {'snapshot_id': 'fakeSnapshotId'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.delete_snapshot_api', ['self.driver.api_executor.delete_snapshot_api',
{'snapshot_id': 'fakeSnapshotId'}, {'snapshot_id': 'fakeSnapshotId'},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.clone_snapshot', ['self.driver.api_executor.clone_snapshot',
{'snapshot_id': 'fakeSnapshotId', {'snapshot_id': 'fakeSnapshotId',
'new_sharename': 'fakeNewShareName'}, 'new_sharename': 'fakeNewShareName'},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.clone_snapshot', ['self.driver.api_executor.clone_snapshot',
{'snapshot_id': 'fakeSnapshotId', {'snapshot_id': 'fakeSnapshotId',
'new_sharename': 'fakeNewShareName'}, 'new_sharename': 'fakeNewShareName'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.edit_share', ['self.driver.api_executor.edit_share',
{'share_dict': {"sharename": 'fakeVolId', {'share_dict': {"sharename": 'fakeVolId',
"old_sharename": 'fakeVolId', "old_sharename": 'fakeVolId',
"new_size": 100}}, "new_size": 100,
"share_proto": "NFS"}},
fakes.FakeEsResCodeNegativeResponse(), fakes.FakeEsResCodeNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.edit_share', ['self.driver.api_executor.edit_share',
{'share_dict': {"sharename": 'fakeVolId', {'share_dict': {"sharename": 'fakeVolId',
"old_sharename": 'fakeVolId', "old_sharename": 'fakeVolId',
"new_size": 100}}, "new_size": 100,
"share_proto": "NFS"}},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.add_host', ['self.driver.api_executor.add_host',
{'hostname': 'fakeHostName', {'hostname': 'fakeHostName',
'ipv4': 'fakeIpV4'}, 'ipv4': 'fakeIpV4'},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.add_host', ['self.driver.api_executor.add_host',
{'hostname': 'fakeHostName', {'hostname': 'fakeHostName',
'ipv4': 'fakeIpV4'}, 'ipv4': 'fakeIpV4'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.get_host_list', ['self.driver.api_executor.get_host_list',
{}, {},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.get_host_list', ['self.driver.api_executor.get_host_list',
{}, {},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.set_nfs_access', ['self.driver.api_executor.set_nfs_access',
{'sharename': 'fakeShareName', {'sharename': 'fakeShareName',
'access': 'fakeAccess', 'access': 'fakeAccess',
'host_name': 'fakeHostName'}, 'host_name': 'fakeHostName'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.set_nfs_access', ['self.driver.api_executor.set_nfs_access',
{'sharename': 'fakeShareName', {'sharename': 'fakeShareName',
'access': 'fakeAccess', 'access': 'fakeAccess',
'host_name': 'fakeHostName'}, 'host_name': 'fakeHostName'},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseEs()], fakes.FakeGetBasicInfoResponseEs_1_1_3()],
['self.driver.api_executor.get_snapshot_info', ['self.driver.api_executor.get_snapshot_info',
{'snapshot_name': 'fakeSnapshoName', {'snapshot_name': 'fakeSnapshoName',
'lun_index': 'fakeLunIndex'}, 'lun_index': 'fakeLunIndex'},
fakes.FakeAuthPassFailResponse(), fakes.FakeAuthPassFailResponse(),
fakes.FakeGetBasicInfoResponseTs()], fakes.FakeGetBasicInfoResponseTs_4_3_0()],
['self.driver.api_executor.get_snapshot_info', ['self.driver.api_executor.get_snapshot_info',
{'snapshot_name': 'fakeSnapshoName', {'snapshot_name': 'fakeSnapshoName',
'lun_index': 'fakeLunIndex'}, 'lun_index': 'fakeLunIndex'},
fakes.FakeResultNegativeResponse(), fakes.FakeResultNegativeResponse(),
fakes.FakeGetBasicInfoResponseTs()]) fakes.FakeGetBasicInfoResponseTs_4_3_0()])
def test_get_snapshot_info_ts_with_fail_response( def test_get_snapshot_info_ts_with_fail_response(
self, api, dict_parm, self, api, dict_parm,
fake_fail_response, fake_basic_info): 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,
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', self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin',
'qnapadmin', 'Storage Pool 1') 'qnapadmin', 'Storage Pool 1')
self.mock_object(time, 'sleep')
self.assertRaises( self.assertRaises(
exception.ShareBackendException, exception.ShareBackendException,
eval(api), eval(api),

View File

@ -23,8 +23,11 @@ import ddt
import mock import mock
from oslo_config import cfg from oslo_config import cfg
import six import six
import time
from eventlet import greenthread
from manila import exception from manila import exception
from manila.share.drivers.qnap import api
from manila.share.drivers.qnap import qnap from manila.share.drivers.qnap import qnap
from manila import test from manila import test
from manila.tests import fake_share from manila.tests import fake_share
@ -86,7 +89,8 @@ class QnapShareDriverLoginTestCase(QnapShareDriverBaseTestCase):
def test_do_setup_positive(self, mng_url, port, ssl): def test_do_setup_positive(self, mng_url, port, ssl):
"""Test do_setup with http://1.2.3.4:8080.""" """Test do_setup with http://1.2.3.4:8080."""
fake_login_response = fakes.FakeLoginResponse() 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: if ssl:
mock_connection = six.moves.http_client.HTTPSConnection mock_connection = six.moves.http_client.HTTPSConnection
else: else:
@ -117,13 +121,12 @@ class QnapShareDriverLoginTestCase(QnapShareDriverBaseTestCase):
self.assertEqual(port, self.driver.api_executor.port) self.assertEqual(port, self.driver.api_executor.port)
self.assertEqual(ssl, self.driver.api_executor.ssl) self.assertEqual(ssl, self.driver.api_executor.ssl)
@ddt.data(fakes.FakeGetBasicInfoResponseTs(), @ddt.data(fakes.FakeGetBasicInfoResponseTs_4_3_0(),
fakes.FakeGetBasicInfoResponseTesTs(), fakes.FakeGetBasicInfoResponseTesTs_4_3_0(),
fakes.FakeGetBasicInfoResponseTesEs()) fakes.FakeGetBasicInfoResponseTesEs_1_1_3())
def test_do_setup_positive_with_diff_nas(self, fake_basic_info): def test_do_setup_positive_with_diff_nas(self, fake_basic_info):
"""Test do_setup with different NAS model.""" """Test do_setup with different NAS model."""
fake_login_response = fakes.FakeLoginResponse() fake_login_response = fakes.FakeLoginResponse()
# fake_get_basic_info_response_ts = FakeGetBasicInfoResponseTs()
mock_connection = six.moves.http_client.HTTPSConnection mock_connection = six.moves.http_client.HTTPSConnection
mock_connection.return_value.getresponse.side_effect = [ mock_connection.return_value.getresponse.side_effect = [
fake_login_response, fake_login_response,
@ -140,6 +143,64 @@ class QnapShareDriverLoginTestCase(QnapShareDriverBaseTestCase):
self.assertEqual('443', self.driver.api_executor.port) self.assertEqual('443', self.driver.api_executor.port)
self.assertTrue(self.driver.api_executor.ssl) 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): def test_do_setup_with_exception(self):
"""Test do_setup with exception.""" """Test do_setup with exception."""
fake_login_response = fakes.FakeLoginResponse() fake_login_response = fakes.FakeLoginResponse()
@ -243,28 +304,81 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase):
mock_get_location_path): mock_get_location_path):
"""Test create share.""" """Test create share."""
mock_api_executor = qnap.QnapShareDriver._create_api_executor 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_gen_random_name.return_value = 'fakeShareName'
mock_api_executor.return_value.create_share.return_value = ( mock_api_executor.return_value.create_share.return_value = (
'fakeCreateShareId') 'fakeCreateShareId')
mock_get_location_path.return_value = None mock_get_location_path.return_value = None
mock_private_storage = mock.Mock() mock_private_storage = mock.Mock()
self.mock_object(greenthread, 'sleep')
self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin', self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin',
'qnapadmin', 'Storage Pool 1', 'qnapadmin', 'Storage Pool 1',
private_storage=mock_private_storage) private_storage=mock_private_storage)
self.driver.create_share('context', self.share) self.driver.create_share('context', self.share)
mock_api_executor.return_value.get_share_info.assert_called_once_with( mock_api_return = mock_api_executor.return_value
'Storage Pool 1', expected_call_list = [
vol_label='fakeShareName') 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( mock_api_executor.return_value.create_share.assert_called_once_with(
self.share, self.share,
self.driver.configuration.qnap_poolname, self.driver.configuration.qnap_poolname,
'fakeShareName', 'fakeShareName',
'NFS') 'NFS')
mock_get_location_path.assert_called_once_with( 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): def test_delete_share_positive(self):
"""Test delete share with fake_share.""" """Test delete share with fake_share."""
@ -323,7 +437,7 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase):
self.get_share_info_return_value()) self.get_share_info_return_value())
mock_api_executor.return_value.edit_share.return_value = None mock_api_executor.return_value.edit_share.return_value = None
mock_private_storage = mock.Mock() 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', self._do_setup('http://1.2.3.4:8080', '1.2.3.4', 'admin',
'qnapadmin', 'Storage Pool 1', 'qnapadmin', 'Storage Pool 1',
@ -331,9 +445,10 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase):
self.driver.extend_share(self.share, 100, share_server=None) self.driver.extend_share(self.share, 100, share_server=None)
expect_share_dict = { expect_share_dict = {
"sharename": 'fakeVolId', 'sharename': 'fakeVolName',
"old_sharename": 'fakeVolId', 'old_sharename': 'fakeVolName',
"new_size": 100, 'new_size': 100,
'share_proto': 'NFS'
} }
mock_api_executor.return_value.edit_share.assert_called_once_with( mock_api_executor.return_value.edit_share.assert_called_once_with(
expect_share_dict) expect_share_dict)
@ -455,10 +570,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase):
mock_api_executor = qnap.QnapShareDriver._create_api_executor mock_api_executor = qnap.QnapShareDriver._create_api_executor
mock_gen_random_name.return_value = 'fakeShareName' mock_gen_random_name.return_value = 'fakeShareName'
mock_api_executor.return_value.get_share_info.side_effect = ( mock_api_executor.return_value.get_share_info.side_effect = [
None, self.get_share_info_return_value()) None, self.get_share_info_return_value()]
mock_api_executor.return_value.clone_snapshot.return_value = (
None)
mock_private_storage = mock.Mock() mock_private_storage = mock.Mock()
mock_private_storage.get.return_value = 'fakeSnapshotId' mock_private_storage.get.return_value = 'fakeSnapshotId'
mock_share_api.return_value.get.return_value = {'size': 10} 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_gen_random_name.return_value = 'fakeShareName'
mock_api_executor = qnap.QnapShareDriver._create_api_executor mock_api_executor = qnap.QnapShareDriver._create_api_executor
mock_api_executor.return_value.get_share_info.side_effect = ( mock_api_executor.return_value.get_share_info.side_effect = [
None, self.get_share_info_return_value()) None, self.get_share_info_return_value()]
mock_api_executor.return_value.clone_snapshot.return_value = (
None)
mock_private_storage = mock.Mock() 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_share_api.return_value.get.return_value = {'size': 5}
mock_api_executor.return_value.edit_share.return_value = ( mock_api_executor.return_value.edit_share.return_value = (
None) None)
@ -525,7 +636,8 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase):
expect_share_dict = { expect_share_dict = {
'sharename': 'fakeShareName', 'sharename': 'fakeShareName',
'old_sharename': 'fakeShareName', 'old_sharename': 'fakeShareName',
'new_size': 10 'new_size': 10,
'share_proto': 'NFS'
} }
mock_api_return.edit_share.assert_called_once_with( mock_api_return.edit_share.assert_called_once_with(
expect_share_dict) expect_share_dict)
@ -548,6 +660,67 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase):
snapshot=fake_snapshot, snapshot=fake_snapshot,
share_server=None) 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') @mock.patch.object(qnap.QnapShareDriver, '_allow_access')
def test_update_access_allow_access( def test_update_access_allow_access(
self, mock_allow_access): self, mock_allow_access):
@ -644,7 +817,28 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase):
mock_api_return.get_specific_volinfo.assert_called_once_with( mock_api_return.get_specific_volinfo.assert_called_once_with(
'fakeNo') 'fakeNo')
mock_get_location_path.assert_called_once_with( 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): def test_manage_existing_nfs_without_export_locations(self):
share = fake_share.fake_share( share = fake_share.fake_share(
@ -820,14 +1014,15 @@ class QnapShareDriverTestCase(QnapShareDriverBaseTestCase):
} }
self.assertEqual( self.assertEqual(
expect_result, self.driver._get_location_path( expect_result, self.driver._get_location_path(
'fakeShareName', 'NFS', 'fakeIp')) 'fakeShareName', 'NFS', 'fakeIp', 'fakeVolId'))
self.assertRaises( self.assertRaises(
exception.InvalidInput, exception.InvalidInput,
self.driver._get_location_path, self.driver._get_location_path,
share_name='fakeShareName', share_name='fakeShareName',
share_proto='fakeProto', share_proto='fakeProto',
ip='fakeIp') ip='fakeIp',
vol_id='fakeVolId')
def test_update_share_stats(self): def test_update_share_stats(self):
"""Test update share stats.""" """Test update share stats."""

View File

@ -0,0 +1,4 @@
---
features:
- |
QNAP Manila driver adds support for QES fw 1.1.4.