Merge "Remove race condition from lvextend"
This commit is contained in:
commit
f38c6c87be
|
@ -94,6 +94,10 @@ class NoFibreChannelVolumeDeviceFound(BrickException):
|
|||
message = _("Unable to find a Fibre Channel volume device.")
|
||||
|
||||
|
||||
class VolumeNotDeactivated(BrickException):
|
||||
message = _('Volume %(name)s was not deactivated in time.')
|
||||
|
||||
|
||||
class VolumeDeviceNotFound(BrickException):
|
||||
message = _("Volume device not found at %(device)s.")
|
||||
|
||||
|
|
|
@ -616,6 +616,20 @@ class LVM(executor.Executor):
|
|||
return name
|
||||
return '_' + name
|
||||
|
||||
def _lv_is_active(self, name):
|
||||
cmd = LVM.LVM_CMD_PREFIX + ['lvdisplay', '--noheading', '-C', '-o',
|
||||
'Attr', '%s/%s' % (self.vg_name, name)]
|
||||
out, _err = self._execute(*cmd,
|
||||
root_helper=self._root_helper,
|
||||
run_as_root=True)
|
||||
if out:
|
||||
out = out.strip()
|
||||
# An example output might be '-wi-a----'; the 4th index specifies
|
||||
# the status of the volume. 'a' for active, '-' for inactive.
|
||||
if (out[4] == 'a'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def deactivate_lv(self, name):
|
||||
lv_path = self.vg_name + '/' + self._mangle_lv_name(name)
|
||||
cmd = ['lvchange', '-a', 'n']
|
||||
|
@ -631,6 +645,21 @@ class LVM(executor.Executor):
|
|||
LOG.error(_LE('StdErr :%s'), err.stderr)
|
||||
raise
|
||||
|
||||
# Wait until lv is deactivated to return in
|
||||
# order to prevent a race condition.
|
||||
self._wait_for_volume_deactivation(name)
|
||||
|
||||
@utils.retry(exceptions=exception.VolumeNotDeactivated, retries=3,
|
||||
backoff_rate=1)
|
||||
def _wait_for_volume_deactivation(self, name):
|
||||
LOG.debug("Checking to see if volume %s has been deactivated.",
|
||||
name)
|
||||
if self._lv_is_active(name):
|
||||
LOG.debug("Volume %s is still active.", name)
|
||||
raise exception.VolumeNotDeactivated(name=name)
|
||||
else:
|
||||
LOG.debug("Volume %s has been deactivated.", name)
|
||||
|
||||
def activate_lv(self, name, is_snapshot=False, permanent=False):
|
||||
"""Ensure that logical volume/snapshot logical volume is activated.
|
||||
|
||||
|
|
|
@ -364,3 +364,29 @@ class BrickLvmTestCase(base.TestCase):
|
|||
self.vg.vg_name = "test-volumes"
|
||||
self.vg.extend_volume("test", "2G")
|
||||
self.assertFalse(self.vg.deactivate_lv.called)
|
||||
|
||||
def test_lv_deactivate(self):
|
||||
with mock.patch.object(self.vg, '_execute'):
|
||||
is_active_mock = mock.Mock()
|
||||
is_active_mock.return_value = False
|
||||
self.vg._lv_is_active = is_active_mock
|
||||
self.vg.create_volume('test', '1G')
|
||||
self.vg.deactivate_lv('test')
|
||||
|
||||
def test_lv_deactivate_timeout(self):
|
||||
with mock.patch.object(self.vg, '_execute'):
|
||||
is_active_mock = mock.Mock()
|
||||
is_active_mock.return_value = True
|
||||
self.vg._lv_is_active = is_active_mock
|
||||
self.vg.create_volume('test', '1G')
|
||||
self.assertRaises(exception.VolumeNotDeactivated,
|
||||
self.vg.deactivate_lv, 'test')
|
||||
|
||||
def test_lv_is_active(self):
|
||||
self.vg.create_volume('test', '1G')
|
||||
with mock.patch.object(self.vg, '_execute',
|
||||
return_value=['owi-a---', '']):
|
||||
self.assertTrue(self.vg._lv_is_active('test'))
|
||||
with mock.patch.object(self.vg, '_execute',
|
||||
return_value=['owi-----', '']):
|
||||
self.assertFalse(self.vg._lv_is_active('test'))
|
||||
|
|
Loading…
Reference in New Issue