diff --git a/cinder/brick/local_dev/lvm.py b/cinder/brick/local_dev/lvm.py index 47e6d5b9f46..c6576df4118 100644 --- a/cinder/brick/local_dev/lvm.py +++ b/cinder/brick/local_dev/lvm.py @@ -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. diff --git a/cinder/tests/brick/test_brick_lvm.py b/cinder/tests/brick/test_brick_lvm.py index 83183b59e6f..92c30649bae 100644 --- a/cinder/tests/brick/test_brick_lvm.py +++ b/cinder/tests/brick/test_brick_lvm.py @@ -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() diff --git a/cinder/volume/drivers/lvm.py b/cinder/volume/drivers/lvm.py index c33532b9e91..f1b5078d3b6 100644 --- a/cinder/volume/drivers/lvm.py +++ b/cinder/volume/drivers/lvm.py @@ -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, diff --git a/etc/cinder/rootwrap.d/volume.filters b/etc/cinder/rootwrap.d/volume.filters index f9e46d32e8c..c30aa089413 100644 --- a/etc/cinder/rootwrap.d/volume.filters +++ b/etc/cinder/rootwrap.d/volume.filters @@ -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 ' +lvchange: CommandFilter, lvchange, root + # cinder/volume/driver.py: 'iscsiadm', '-m', 'discovery', '-t',... # cinder/volume/driver.py: 'iscsiadm', '-m', 'node', '-T', ... iscsiadm: CommandFilter, iscsiadm, root