self.vg_uuid = None
self.vg_thin_pool = None
self.vg_thin_pool_size = 0
+ self._supports_snapshot_lv_activation = None
+ self._supports_lvchange_ignoreskipactivation = None
if create_vg and physical_volumes is not None:
self.pv_list = physical_volumes
return []
@staticmethod
- def supports_thin_provisioning(root_helper):
- """Static method to check for thin LVM support on a system.
+ def get_lvm_version(root_helper):
+ """Static method to get LVM version from system.
:param root_helper: root_helper to use for execute
- :returns: True if supported, False otherwise
+ :returns: version 3-tuple
"""
+
cmd = ['vgs', '--version']
(out, err) = putils.execute(*cmd,
root_helper=root_helper,
version_filter = "(\d+)\.(\d+)\.(\d+).*"
r = re.search(version_filter, version)
version_tuple = tuple(map(int, r.group(1, 2, 3)))
- if version_tuple >= (2, 2, 95):
- return True
- return False
+ return version_tuple
+
+ @staticmethod
+ def supports_thin_provisioning(root_helper):
+ """Static method to check for thin LVM support on a system.
+
+ :param root_helper: root_helper to use for execute
+ :returns: True if supported, False otherwise
+
+ """
+
+ return LVM.get_lvm_version(root_helper) >= (2, 2, 95)
+
+ @property
+ def supports_snapshot_lv_activation(self):
+ """Property indicating whether snap activation changes are supported.
+
+ Check for LVM version > 2.02.91.
+ (LVM2 git: e8a40f6 Allow to activate snapshot)
+
+ :returns: True/False indicating support
+ """
+
+ if self._supports_snapshot_lv_activation is not None:
+ return self._supports_snapshot_lv_activation
+
+ self._supports_snapshot_lv_activation = (
+ self.get_lvm_version(self._root_helper) >= (2, 2, 91))
+
+ return self._supports_snapshot_lv_activation
+
+ @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):
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, is_snapshot=False):
+ """Ensure that logical volume/snapshot logical volume is activated.
+
+ :param name: Name of LV to activate
+ :raises: putils.ProcessExecutionError
+ """
+
+ # This is a no-op if requested for a snapshot on a version
+ # of LVM that doesn't support snapshot activation.
+ # (Assume snapshot LV is always active.)
+ if is_snapshot and not self.supports_snapshot_lv_activation:
+ return
+
+ lv_path = self.vg_name + '/' + self._mangle_lv_name(name)
+
+ # Must pass --yes to activate both the snap LV and its origin LV.
+ # Otherwise lvchange asks if you would like to do this interactively,
+ # and fails.
+ cmd = ['lvchange', '-a', 'y', '--yes']
+
+ 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.
return (" LVM version: 2.03.00 (2012-03-06)\n", "")
def fake_old_lvm_version(obj, *cmd, **kwargs):
+ # Does not support thin prov or snap activation
return (" LVM version: 2.02.65(2) (2012-03-06)\n", "")
def fake_customised_lvm_version(obj, *cmd, **kwargs):
self.fake_customised_lvm_version)
self.assertTrue(self.vg.supports_thin_provisioning('sudo'))
+ def test_snapshot_lv_activate_support(self):
+ self.vg._supports_snapshot_lv_activation = None
+ self.stubs.Set(processutils, 'execute', self.fake_execute)
+ self.assertTrue(self.vg.supports_snapshot_lv_activation)
+
+ self.vg._supports_snapshot_lv_activation = None
+ self.stubs.Set(processutils, 'execute', self.fake_old_lvm_version)
+ self.assertFalse(self.vg.supports_snapshot_lv_activation)
+
+ self.vg._supports_snapshot_lv_activation = None
+
+ 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.vg._supports_lvchange_ignoreskipactivation = None
+ self.stubs.Set(self.vg, '_execute', lvchange_ign_yes)
+ self.assertTrue(self.vg.supports_lvchange_ignoreskipactivation)
+
+ self.vg._supports_lvchange_ignoreskipactivation = None
+
+ 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.vg._supports_lvchange_ignoreskipactivation = None
+ self.stubs.Set(self.vg, '_execute', lvchange_ign_no)
+ self.assertFalse(self.vg.supports_lvchange_ignoreskipactivation)
+
+ self.vg._supports_lvchange_ignoreskipactivation = None
+
def test_volume_create_after_thin_creation(self):
"""Test self.vg.vg_thin_pool is set to pool_name
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', '--yes', '-K',
+ 'fake-volumes/my-lv',
+ root_helper='sudo', run_as_root=True)
+
+ self._mox.ReplayAll()
+
+ self.vg.activate_lv('my-lv')
+
+ self._mox.VerifyAll()