LVM: Activate source LV before cloning from it

LVM may be configured to not automatically activate
thin-provisioned LVs.

Ensure they are activated when performing a clone, otherwise
dd will fail as the device does not exist in /dev/mapper/.

Closes-Bug: #1252423

Change-Id: Ibcb946ffe7804b1976bf1b1863c48340c8cc7fd5
This commit is contained in:
Eric Harney 2013-11-18 13:42:37 -05:00
parent 1099f09a2d
commit 01a2199d85
4 changed files with 118 additions and 0 deletions

View File

@ -64,6 +64,7 @@ class LVM(executor.Executor):
self.vg_uuid = None
self.vg_thin_pool = None
self.vg_thin_pool_size = 0
self._supports_lvchange_ignoreskipactivation = None
if create_vg and physical_volumes is not None:
self.pv_list = physical_volumes
@ -146,6 +147,29 @@ class LVM(executor.Executor):
return True
return False
@property
def supports_lvchange_ignoreskipactivation(self):
"""Property indicating whether lvchange can ignore skip activation.
Tests whether lvchange -K/--ignoreactivationskip exists.
"""
if self._supports_lvchange_ignoreskipactivation is not None:
return self._supports_lvchange_ignoreskipactivation
cmd = ['lvchange', '--help']
(out, err) = self._execute(*cmd)
self._supports_lvchange_ignoreskipactivation = False
lines = out.split('\n')
for line in lines:
if '-K' in line and '--ignoreactivationskip' in line:
self._supports_lvchange_ignoreskipactivation = True
break
return self._supports_lvchange_ignoreskipactivation
@staticmethod
def get_all_volumes(root_helper, vg_name=None, no_suffix=True):
"""Static method to get all LV's on a system.
@ -404,6 +428,38 @@ class LVM(executor.Executor):
LOG.error(_('StdErr :%s') % err.stderr)
raise
def _mangle_lv_name(self, name):
# Linux LVM reserves name that starts with snapshot, so that
# such volume name can't be created. Mangle it.
if not name.startswith('snapshot'):
return name
return '_' + name
def activate_lv(self, name):
"""Ensure that logical volume/snapshot logical volume is activated.
:param name: Name of LV to activate
"""
lv_path = self.vg_name + '/' + self._mangle_lv_name(name)
cmd = ['lvchange', '-a', 'y']
if self.supports_lvchange_ignoreskipactivation:
cmd.append('-K')
cmd.append(lv_path)
try:
self._execute(*cmd,
root_helper=self._root_helper,
run_as_root=True)
except putils.ProcessExecutionError as err:
LOG.exception(_('Error activating LV'))
LOG.error(_('Cmd :%s') % err.cmd)
LOG.error(_('StdOut :%s') % err.stdout)
LOG.error(_('StdErr :%s') % err.stderr)
raise
def delete(self, name):
"""Delete logical volume or snapshot.

View File

@ -153,6 +153,48 @@ class BrickLvmTestCase(test.TestCase):
self.fake_customised_lvm_version)
self.assertTrue(self.vg.supports_thin_provisioning('sudo'))
def test_lvchange_ignskipact_support_yes(self):
"""Tests the ability to test support for lvchange -K
Stubs provide output for "lvchange --help".
"""
def lvchange_ign_yes(obj, *args, **kwargs):
return ("""
WARNING: Running as a non-root user. Functionality may be
unavailable.
lvchange: Change the attributes of logical volume(s)
lvchange
[-A|--autobackup y|n]
[-k|--setactivationskip {y|n}]
[-K|--ignoreactivationskip]
[-y|--yes]
[-Z|--zero {y|n}]
LogicalVolume[Path] [LogicalVolume[Path]...]
""", "")
self.stubs.Set(self.vg, '_execute', lvchange_ign_yes)
self.assertTrue(self.vg.supports_lvchange_ignoreskipactivation)
def test_lvchange_ignskipact_support_no(self):
def lvchange_ign_no(obj, *args, **kwargs):
return ("""
WARNING: Running as a non-root user. Functionality may be
unavailable.
lvchange: Change the attributes of logical volume(s)
lvchange
[-A|--autobackup y|n]
[-k|--setactivationskip {y|n}]
[-y|--yes]
[-Z|--zero {y|n}]
LogicalVolume[Path] [LogicalVolume[Path]...]
""", "")
self.stubs.Set(self.vg, '_execute', lvchange_ign_no)
self.assertFalse(self.vg.supports_lvchange_ignoreskipactivation)
def test_volume_create_after_thin_creation(self):
"""Test self.vg.vg_thin_pool is set to pool_name
@ -175,3 +217,16 @@ class BrickLvmTestCase(test.TestCase):
def test_lv_has_snapshot(self):
self.assertTrue(self.vg.lv_has_snapshot('fake-volumes'))
self.assertFalse(self.vg.lv_has_snapshot('test-volumes'))
def test_activate_lv(self):
self._mox.StubOutWithMock(self.vg, '_execute')
self.vg._supports_lvchange_ignoreskipactivation = True
self.vg._execute('lvchange', '-a', 'y', '-K', 'fake-volumes/my-lv',
root_helper='sudo', run_as_root=True)
self._mox.ReplayAll()
self.vg.activate_lv('my-lv')
self._mox.VerifyAll()

View File

@ -166,6 +166,10 @@ class LVMVolumeDriver(driver.VolumeDriver):
self.configuration.lvm_type,
self.configuration.lvm_mirrors)
# Some configurations of LVM do not automatically activate
# ThinLVM snapshot LVs.
self.vg.activate_lv(snapshot['name'])
volutils.copy_volume(self.local_path(snapshot),
self.local_path(volume),
snapshot['volume_size'] * 1024,

View File

@ -30,6 +30,9 @@ lvrename: CommandFilter, lvrename, root
# cinder/volume/driver.py: 'lvextend', '-L' '%(new_size)s', '%(lv_name)s' ...
lvextend: CommandFilter, lvextend, root
# cinder/brick/local_dev/lvm.py: 'lvchange -a y -K <lv>'
lvchange: CommandFilter, lvchange, root
# cinder/volume/driver.py: 'iscsiadm', '-m', 'discovery', '-t',...
# cinder/volume/driver.py: 'iscsiadm', '-m', 'node', '-T', ...
iscsiadm: CommandFilter, iscsiadm, root