Merge "LVM: Fix delete volume error due to lvs failure"

This commit is contained in:
Zuul 2021-04-01 17:58:41 +00:00 committed by Gerrit Code Review
commit 6124c627d6
8 changed files with 78 additions and 11 deletions

View File

@ -286,6 +286,8 @@ class LVM(executor.Executor):
return LVM._supports_pvs_ignoreskippedcluster
@staticmethod
@utils.retry(retry=utils.retry_if_exit_code, retry_param=139, interval=0.5,
backoff_rate=0.5) # Bug#1901783
def get_lv_info(root_helper, vg_name=None, lv_name=None):
"""Retrieve info about LVs (all, in a VG, or a single LV).
@ -653,7 +655,7 @@ class LVM(executor.Executor):
# order to prevent a race condition.
self._wait_for_volume_deactivation(name)
@utils.retry(exceptions=exception.VolumeNotDeactivated, retries=5,
@utils.retry(retry_param=exception.VolumeNotDeactivated, retries=5,
backoff_rate=2)
def _wait_for_volume_deactivation(self, name):
LOG.debug("Checking to see if volume %s has been deactivated.",

View File

@ -231,6 +231,27 @@ class BrickLvmTestCase(test.TestCase):
'sudo', vg_name='fake-vg')
)
@mock.patch('tenacity.nap.sleep', mock.Mock())
@mock.patch.object(brick.putils, 'execute')
def test_get_lv_info_retry(self, exec_mock):
exec_mock.side_effect = (
processutils.ProcessExecutionError('', '', exit_code=139),
('vg name size', ''),
)
self.assertEqual(
[{'name': 'name', 'vg': 'vg', 'size': 'size'}],
self.vg.get_lv_info('sudo', vg_name='vg', lv_name='name')
)
self.assertEqual(2, exec_mock.call_count)
args = ['env', 'LC_ALL=C', 'lvs', '--noheadings', '--unit=g', '-o',
'vg_name,name,size', '--nosuffix', '--readonly', 'vg/name']
if self.configuration.lvm_suppress_fd_warnings:
args.insert(2, 'LVM_SUPPRESS_FD_WARNINGS=1')
lvs_call = mock.call(*args, root_helper='sudo', run_as_root=True)
exec_mock.assert_has_calls([lvs_call, lvs_call])
def test_get_all_physical_volumes(self):
# Filtered VG version
pvs = self.vg.get_all_physical_volumes('sudo', 'fake-vg')

View File

@ -1056,6 +1056,33 @@ class TestRetryDecorator(test.TestCase):
self.assertRaises(WrongException, raise_unexpected_error)
self.assertFalse(mock_sleep.called)
@mock.patch('tenacity.nap.sleep')
def test_retry_exit_code(self, sleep_mock):
exit_code = 5
exception = utils.processutils.ProcessExecutionError
@utils.retry(retry=utils.retry_if_exit_code, retry_param=exit_code)
def raise_retriable_exit_code():
raise exception(exit_code=exit_code)
self.assertRaises(exception, raise_retriable_exit_code)
self.assertEqual(2, sleep_mock.call_count)
sleep_mock.assert_has_calls([mock.call(1), mock.call(2)])
@mock.patch('tenacity.nap.sleep')
def test_retry_exit_code_non_retriable(self, sleep_mock):
exit_code = 5
exception = utils.processutils.ProcessExecutionError
@utils.retry(retry=utils.retry_if_exit_code, retry_param=exit_code)
def raise_non_retriable_exit_code():
raise exception(exit_code=exit_code + 1)
self.assertRaises(exception, raise_non_retriable_exit_code)
sleep_mock.assert_not_called()
@ddt.ddt
class TestCalculateVirtualFree(test.TestCase):

View File

@ -575,8 +575,19 @@ class ComparableMixin(object):
return self._compare(other, lambda s, o: s != o)
def retry(exceptions, interval=1, retries=3, backoff_rate=2,
wait_random=False):
class retry_if_exit_code(tenacity.retry_if_exception):
"""Retry on ProcessExecutionError specific exit codes."""
def __init__(self, codes):
self.codes = (codes,) if isinstance(codes, int) else codes
super(retry_if_exit_code, self).__init__(self._check_exit_code)
def _check_exit_code(self, exc):
return (exc and isinstance(exc, processutils.ProcessExecutionError) and
exc.exit_code in self.codes)
def retry(retry_param, interval=1, retries=3, backoff_rate=2,
wait_random=False, retry=tenacity.retry_if_exception_type):
if retries < 1:
raise ValueError('Retries must be greater than or '
@ -598,7 +609,7 @@ def retry(exceptions, interval=1, retries=3, backoff_rate=2,
after=tenacity.after_log(LOG, logging.DEBUG),
stop=tenacity.stop_after_attempt(retries),
reraise=True,
retry=tenacity.retry_if_exception_type(exceptions),
retry=retry(retry_param),
wait=wait)
return r.call(f, *args, **kwargs)

View File

@ -232,7 +232,7 @@ class HttpClient(object):
raise exception.VolumeBackendAPIException(message=msg)
return rest_response
@utils.retry(exceptions=(requests.ConnectionError,
@utils.retry(retry_param=(requests.ConnectionError,
DellDriverRetryableException))
def get(self, url):
LOG.debug('get: %(url)s', {'url': url})
@ -247,7 +247,7 @@ class HttpClient(object):
raise DellDriverRetryableException()
return rest_response
@utils.retry(exceptions=(requests.ConnectionError,))
@utils.retry(retry_param=(requests.ConnectionError,))
def post(self, url, payload, async_call=False):
LOG.debug('post: %(url)s data: %(payload)s',
{'url': url,
@ -261,7 +261,7 @@ class HttpClient(object):
self.asynctimeout if async_call else self.synctimeout)),
async_call)
@utils.retry(exceptions=(requests.ConnectionError,))
@utils.retry(retry_param=(requests.ConnectionError,))
def put(self, url, payload, async_call=False):
LOG.debug('put: %(url)s data: %(payload)s',
{'url': url,
@ -275,7 +275,7 @@ class HttpClient(object):
self.asynctimeout if async_call else self.synctimeout)),
async_call)
@utils.retry(exceptions=(requests.ConnectionError,))
@utils.retry(retry_param=(requests.ConnectionError,))
def delete(self, url, payload=None, async_call=False):
LOG.debug('delete: %(url)s data: %(payload)s',
{'url': url, 'payload': payload})

View File

@ -200,7 +200,7 @@ class Client(object):
def modify_lun(self):
pass
@cinder_utils.retry(exceptions=const.VNXTargetNotReadyError,
@cinder_utils.retry(retry_param=const.VNXTargetNotReadyError,
interval=15,
retries=5, backoff_rate=1)
def migrate_lun(self, src_id, dst_id,

View File

@ -704,7 +704,7 @@ class AS13000Driver(san.SanISCSIDriver):
request_type=request_type)
@volume_utils.trace
@utils.retry(exceptions=exception.VolumeDriverException,
@utils.retry(retry_param=exception.VolumeDriverException,
interval=1,
retries=3)
def _add_lun_to_target(self, target_name, volume):

View File

@ -0,0 +1,6 @@
---
fixes:
- |
LVM driver `bug #1901783
<https://bugs.launchpad.net/cinder/+bug/1901783>`_: Fix unexpected delete
volume failure due to unexpected exit code 139 on ``lvs`` command call.