ZFSSA iSCSI delete volume with non-existent LUN

Under some circumstances, a volume can exist in cinder for which there
is no corresponding LUN on the ZFSSA. This fix allows deletion of the
volume in such cases, by catching the NOT_FOUND status from the ZFSSA
REST API and translating it to a VolumeNotFound exception, and then
considering that exception to be non-fatal, whilst passing up any
other exception that may be encountered. This way, some other failure
in using the REST API to obtain LUN information (e.g. transient
network failure) will not cause the cinder volume to get blindly
deleted, which would have left behind an orphaned LUN on the ZFSSA.

Change-Id: I332163fa35a2aa3f6f921b805fa97c803ec10724
Closes-Bug: #1537914
This commit is contained in:
iain MacDonnell 2017-07-18 21:35:09 +00:00
parent 583b902511
commit e68b879ef0
3 changed files with 35 additions and 12 deletions

View File

@ -390,6 +390,19 @@ class TestZFSSAISCSIDriver(test.TestCase):
project=lcfg.zfssa_project,
lun=self.test_vol['name'])
def test_delete_volume_with_missing_lun(self):
self.drv.zfssa.get_lun.side_effect = exception.VolumeNotFound(
volume_id=self.test_vol['name'])
self.drv.delete_volume(self.test_vol)
self.drv.zfssa.delete_lun.assert_not_called()
def test_delete_volume_backend_fail(self):
self.drv.zfssa.get_lun.side_effect = \
exception.VolumeBackendAPIException(data='fakemsg')
self.assertRaises(exception.VolumeBackendAPIException,
self.drv.delete_volume,
self.test_vol)
@mock.patch.object(iscsi.ZFSSAISCSIDriver, '_check_origin')
def test_delete_cache_volume(self, _check_origin):
lcfg = self.configuration

View File

@ -327,24 +327,23 @@ class ZFSSAISCSIDriver(driver.ISCSIDriver):
lcfg.zfssa_target_group,
specs)
@utils.trace
def delete_volume(self, volume):
"""Deletes a volume with the given volume['name']."""
LOG.debug('zfssa.delete_volume: name=%s', volume['name'])
lcfg = self.configuration
try:
lun2del = self.zfssa.get_lun(lcfg.zfssa_pool,
lcfg.zfssa_project,
volume['name'])
except exception.VolumeBackendAPIException as ex:
# NOTE(jdg): This will log an error and continue
# if for some reason the volume no longer exists
# on the backend
if 'Error Getting Volume' in ex.message:
LOG.error("Volume ID %s was not found on "
"the zfssa device while attempting "
"delete_volume operation.", volume['id'])
return
except exception.VolumeNotFound:
# Sometimes a volume exists in cinder for which there is no
# corresponding LUN (e.g. LUN create failed). In this case,
# allow deletion to complete (without doing anything on the
# ZFSSA). Any other exception should be passed up.
LOG.warning('No LUN found on ZFSSA corresponding to volume '
'ID %s.', volume['id'])
return
# Delete clone temp snapshot. see create_cloned_volume()
if 'origin' in lun2del and 'id' in volume:

View File

@ -730,7 +730,18 @@ class ZFSSAApi(object):
svc = '/api/storage/v1/pools/' + pool + '/projects/' + \
project + "/luns/" + lun
ret = self.rclient.get(svc)
if ret.status != restclient.Status.OK:
if ret.status == restclient.Status.NOT_FOUND:
# Sometimes a volume exists in cinder for which there is no
# corresponding LUN (e.g. LUN create failed). In this case,
# allow deletion to complete (without doing anything on the
# ZFSSA). Any other exception should be passed up.
LOG.warning('LUN with name %(lun)s not found in project '
'%(project)s, pool %(pool)s.',
{'lun': lun,
'project': project,
'pool': pool})
raise exception.VolumeNotFound(volume_id=lun)
elif ret.status != restclient.Status.OK:
exception_msg = (_('Error Getting '
'Volume: %(lun)s on '
'Pool: %(pool)s '
@ -743,7 +754,7 @@ class ZFSSAApi(object):
'ret.status': ret.status,
'ret.data': ret.data})
LOG.error(exception_msg)
raise exception.VolumeNotFound(volume_id=lun)
raise exception.VolumeBackendAPIException(data=exception_msg)
val = json.loads(ret.data)
ret = {