neil / rpms / python-blivet

Forked from rpms/python-blivet a year ago
Clone

Blame SOURCES/0026-Tell-lvm-to-ignore-skip-activation-flag-on-lvs-we-are-removing-or-otherwise-modifying.patch

0b05ab
From c85a80ca54eabb1cf2458a3e17b3472ba2eb0914 Mon Sep 17 00:00:00 2001
0b05ab
From: David Lehman <dlehman@redhat.com>
0b05ab
Date: Fri, 1 Nov 2019 12:07:43 -0400
0b05ab
Subject: [PATCH 1/2] Override LVM skip-activation to allow for thorough
0b05ab
 removal.
0b05ab
0b05ab
When we have been told to remove the LV or manage the formatting we
0b05ab
must tell LVM to ignore the skip-activation bit. Otherwise we have
0b05ab
no way to properly perform the requested management.
0b05ab
0b05ab
Resolves: rhbz#1766498
0b05ab
---
0b05ab
 blivet/deviceaction.py         | 35 ++++++++++++++++++++++++++++++++++
0b05ab
 blivet/devices/lvm.py          | 12 ++++--------
0b05ab
 tests/action_test.py           | 16 ++++++++++++++++
0b05ab
 tests/devices_test/lvm_test.py | 29 ++++++++++++++++++++++++++++
0b05ab
 4 files changed, 84 insertions(+), 8 deletions(-)
0b05ab
0b05ab
diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py
0b05ab
index 14a06ff0..57115662 100644
0b05ab
--- a/blivet/deviceaction.py
0b05ab
+++ b/blivet/deviceaction.py
0b05ab
@@ -393,10 +393,29 @@ class ActionDestroyDevice(DeviceAction):
0b05ab
 
0b05ab
         super(ActionDestroyDevice, self)._check_device_dependencies()
0b05ab
 
0b05ab
+    def apply(self):
0b05ab
+        """ apply changes related to the action to the device(s) """
0b05ab
+        if self._applied:
0b05ab
+            return
0b05ab
+
0b05ab
+        if hasattr(self.device, 'ignore_skip_activation'):
0b05ab
+            self.device.ignore_skip_activation += 1
0b05ab
+
0b05ab
+        super(ActionDestroyDevice, self).apply()
0b05ab
+
0b05ab
     def execute(self, callbacks=None):
0b05ab
         super(ActionDestroyDevice, self).execute(callbacks=callbacks)
0b05ab
         self.device.destroy()
0b05ab
 
0b05ab
+    def cancel(self):
0b05ab
+        if not self._applied:
0b05ab
+            return
0b05ab
+
0b05ab
+        if hasattr(self.device, 'ignore_skip_activation'):
0b05ab
+            self.device.ignore_skip_activation -= 1
0b05ab
+
0b05ab
+        super(ActionDestroyDevice, self).cancel()
0b05ab
+
0b05ab
     def requires(self, action):
0b05ab
         """ Return True if self requires action.
0b05ab
 
0b05ab
@@ -715,6 +734,9 @@ class ActionDestroyFormat(DeviceAction):
0b05ab
             return
0b05ab
 
0b05ab
         self.device.format = None
0b05ab
+        if hasattr(self.device, 'ignore_skip_activation'):
0b05ab
+            self.device.ignore_skip_activation += 1
0b05ab
+
0b05ab
         super(ActionDestroyFormat, self).apply()
0b05ab
 
0b05ab
     def execute(self, callbacks=None):
0b05ab
@@ -739,6 +761,8 @@ class ActionDestroyFormat(DeviceAction):
0b05ab
             return
0b05ab
 
0b05ab
         self.device.format = self.orig_format
0b05ab
+        if hasattr(self.device, 'ignore_skip_activation'):
0b05ab
+            self.device.ignore_skip_activation -= 1
0b05ab
         super(ActionDestroyFormat, self).cancel()
0b05ab
 
0b05ab
     @property
0b05ab
@@ -834,6 +858,9 @@ class ActionResizeFormat(DeviceAction):
0b05ab
             return
0b05ab
 
0b05ab
         self.device.format.target_size = self._target_size
0b05ab
+        if hasattr(self.device, 'ignore_skip_activation'):
0b05ab
+            self.device.ignore_skip_activation += 1
0b05ab
+
0b05ab
         super(ActionResizeFormat, self).apply()
0b05ab
 
0b05ab
     def execute(self, callbacks=None):
0b05ab
@@ -854,6 +881,9 @@ class ActionResizeFormat(DeviceAction):
0b05ab
             return
0b05ab
 
0b05ab
         self.device.format.target_size = self.orig_size
0b05ab
+        if hasattr(self.device, 'ignore_skip_activation'):
0b05ab
+            self.device.ignore_skip_activation -= 1
0b05ab
+
0b05ab
         super(ActionResizeFormat, self).cancel()
0b05ab
 
0b05ab
     def requires(self, action):
0b05ab
@@ -1056,6 +1086,9 @@ class ActionConfigureFormat(DeviceAction):
0b05ab
             return
0b05ab
 
0b05ab
         setattr(self.device.format, self.attr, self.new_value)
0b05ab
+        if hasattr(self.device, 'ignore_skip_activation'):
0b05ab
+            self.device.ignore_skip_activation += 1
0b05ab
+
0b05ab
         super(ActionConfigureFormat, self).apply()
0b05ab
 
0b05ab
     def cancel(self):
0b05ab
@@ -1063,6 +1096,8 @@ class ActionConfigureFormat(DeviceAction):
0b05ab
             return
0b05ab
 
0b05ab
         setattr(self.device.format, self.attr, self.old_value)
0b05ab
+        if hasattr(self.device, 'ignore_skip_activation'):
0b05ab
+            self.device.ignore_skip_activation -= 1
0b05ab
 
0b05ab
     def execute(self, callbacks=None):
0b05ab
         super(ActionConfigureFormat, self).execute(callbacks=callbacks)
0b05ab
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
0b05ab
index 06191110..58adf5cf 100644
0b05ab
--- a/blivet/devices/lvm.py
0b05ab
+++ b/blivet/devices/lvm.py
0b05ab
@@ -628,6 +628,8 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
0b05ab
         self.uuid = uuid
0b05ab
         self.seg_type = seg_type or "linear"
0b05ab
         self._raid_level = None
0b05ab
+        self.ignore_skip_activation = 0
0b05ab
+
0b05ab
         if self.seg_type in lvm.raid_seg_types:
0b05ab
             self._raid_level = lvm.raid_levels.raid_level(self.seg_type)
0b05ab
         else:
0b05ab
@@ -1367,12 +1369,6 @@ class LVMSnapshotMixin(object):
0b05ab
         # the old snapshot cannot be setup and torn down
0b05ab
         pass
0b05ab
 
0b05ab
-    def _setup(self, orig=False):
0b05ab
-        """ Open, or set up, a device. """
0b05ab
-        log_method_call(self, self.name, orig=orig, status=self.status,
0b05ab
-                        controllable=self.controllable)
0b05ab
-        blockdev.lvm.lvactivate(self.vg.name, self._name, ignore_skip=True)
0b05ab
-
0b05ab
     @old_snapshot_specific
0b05ab
     def teardown(self, recursive=False):
0b05ab
         # the old snapshot cannot be setup and torn down
0b05ab
@@ -1969,12 +1965,12 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin
0b05ab
     def display_lv_name(self):
0b05ab
         return self.lvname
0b05ab
 
0b05ab
-    @type_specific
0b05ab
     def _setup(self, orig=False):
0b05ab
         """ Open, or set up, a device. """
0b05ab
         log_method_call(self, self.name, orig=orig, status=self.status,
0b05ab
                         controllable=self.controllable)
0b05ab
-        blockdev.lvm.lvactivate(self.vg.name, self._name)
0b05ab
+        ignore_skip_activation = self.is_snapshot_lv or self.ignore_skip_activation > 0
0b05ab
+        blockdev.lvm.lvactivate(self.vg.name, self._name, ignore_skip=ignore_skip_activation)
0b05ab
 
0b05ab
     @type_specific
0b05ab
     def _pre_create(self):
0b05ab
diff --git a/tests/action_test.py b/tests/action_test.py
0b05ab
index 101d5a21..24ed10b2 100644
0b05ab
--- a/tests/action_test.py
0b05ab
+++ b/tests/action_test.py
0b05ab
@@ -1025,12 +1025,28 @@ class DeviceActionTestCase(StorageTestCase):
0b05ab
         # ActionDestroyFormat
0b05ab
         original_format = lv_root.format
0b05ab
         action = ActionDestroyFormat(lv_root)
0b05ab
+        orig_ignore_skip = lv_root.ignore_skip_activation
0b05ab
         self.assertEqual(lv_root.format, original_format)
0b05ab
         self.assertNotEqual(lv_root.format.type, None)
0b05ab
         action.apply()
0b05ab
         self.assertEqual(lv_root.format.type, None)
0b05ab
+        self.assertEqual(lv_root.ignore_skip_activation, orig_ignore_skip + 1)
0b05ab
         action.cancel()
0b05ab
         self.assertEqual(lv_root.format, original_format)
0b05ab
+        self.assertEqual(lv_root.ignore_skip_activation, orig_ignore_skip)
0b05ab
+
0b05ab
+        # ActionDestroyDevice
0b05ab
+        action1 = ActionDestroyFormat(lv_root)
0b05ab
+        orig_ignore_skip = lv_root.ignore_skip_activation
0b05ab
+        action1.apply()
0b05ab
+        self.assertEqual(lv_root.ignore_skip_activation, orig_ignore_skip + 1)
0b05ab
+        action2 = ActionDestroyDevice(lv_root)
0b05ab
+        action2.apply()
0b05ab
+        self.assertEqual(lv_root.ignore_skip_activation, orig_ignore_skip + 2)
0b05ab
+        action2.cancel()
0b05ab
+        self.assertEqual(lv_root.ignore_skip_activation, orig_ignore_skip + 1)
0b05ab
+        action1.cancel()
0b05ab
+        self.assertEqual(lv_root.ignore_skip_activation, orig_ignore_skip)
0b05ab
 
0b05ab
         sdc = self.storage.devicetree.get_device_by_name("sdc")
0b05ab
         sdc.format = None
0b05ab
diff --git a/tests/devices_test/lvm_test.py b/tests/devices_test/lvm_test.py
0b05ab
index 76a3a5db..c4c50748 100644
0b05ab
--- a/tests/devices_test/lvm_test.py
0b05ab
+++ b/tests/devices_test/lvm_test.py
0b05ab
@@ -360,6 +360,35 @@ class LVMDeviceTest(unittest.TestCase):
0b05ab
             with six.assertRaisesRegex(self, ValueError, "The volume group testvg cannot be created."):
0b05ab
                 LVMVolumeGroupDevice("testvg", parents=[pv, pv2])
0b05ab
 
0b05ab
+    def test_skip_activate(self):
0b05ab
+        pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"),
0b05ab
+                           size=Size("1 GiB"), exists=True)
0b05ab
+        vg = LVMVolumeGroupDevice("testvg", parents=[pv], exists=True)
0b05ab
+        lv = LVMLogicalVolumeDevice("data_lv", parents=[vg], size=Size("500 MiB"), exists=True)
0b05ab
+
0b05ab
+        with patch("blivet.devices.lvm.blockdev.lvm") as lvm:
0b05ab
+            with patch.object(lv, "_pre_setup"):
0b05ab
+                lv.setup()
0b05ab
+                self.assertTrue(lvm.lvactivate.called_with(vg.name, lv.lvname, ignore_skip=False))
0b05ab
+
0b05ab
+        lv.ignore_skip_activation += 1
0b05ab
+        with patch("blivet.devices.lvm.blockdev.lvm") as lvm:
0b05ab
+            with patch.object(lv, "_pre_setup"):
0b05ab
+                lv.setup()
0b05ab
+                self.assertTrue(lvm.lvactivate.called_with(vg.name, lv.lvname, ignore_skip=True))
0b05ab
+
0b05ab
+        lv.ignore_skip_activation += 1
0b05ab
+        with patch("blivet.devices.lvm.blockdev.lvm") as lvm:
0b05ab
+            with patch.object(lv, "_pre_setup"):
0b05ab
+                lv.setup()
0b05ab
+                self.assertTrue(lvm.lvactivate.called_with(vg.name, lv.lvname, ignore_skip=True))
0b05ab
+
0b05ab
+        lv.ignore_skip_activation -= 2
0b05ab
+        with patch("blivet.devices.lvm.blockdev.lvm") as lvm:
0b05ab
+            with patch.object(lv, "_pre_setup"):
0b05ab
+                lv.setup()
0b05ab
+                self.assertTrue(lvm.lvactivate.called_with(vg.name, lv.lvname, ignore_skip=False))
0b05ab
+
0b05ab
 
0b05ab
 class TypeSpecificCallsTest(unittest.TestCase):
0b05ab
     def test_type_specific_calls(self):
0b05ab
-- 
0b05ab
2.24.1
0b05ab
0b05ab
0b05ab
From 0e19f91ff0917b7c498cdc2e6d5484847cf18cee Mon Sep 17 00:00:00 2001
0b05ab
From: David Lehman <dlehman@redhat.com>
0b05ab
Date: Tue, 17 Dec 2019 14:43:02 -0500
0b05ab
Subject: [PATCH 2/2] Make sure LVs are writable before wiping.
0b05ab
0b05ab
Related: rhbz#1766498
0b05ab
---
0b05ab
 blivet/deviceaction.py   |  3 +++
0b05ab
 blivet/devicelibs/lvm.py | 18 ++++++++++++++++++
0b05ab
 blivet/devices/lvm.py    |  4 ++++
0b05ab
 3 files changed, 25 insertions(+)
0b05ab
0b05ab
diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py
0b05ab
index 57115662..ac89365b 100644
0b05ab
--- a/blivet/deviceaction.py
0b05ab
+++ b/blivet/deviceaction.py
0b05ab
@@ -745,6 +745,9 @@ class ActionDestroyFormat(DeviceAction):
0b05ab
         super(ActionDestroyFormat, self).execute(callbacks=callbacks)
0b05ab
         status = self.device.status
0b05ab
         self.device.setup(orig=True)
0b05ab
+        if hasattr(self.device, 'set_rw'):
0b05ab
+            self.device.set_rw()
0b05ab
+
0b05ab
         self.format.destroy()
0b05ab
         udev.settle()
0b05ab
         if isinstance(self.device, PartitionDevice) and self.device.disklabel_supported:
0b05ab
diff --git a/blivet/devicelibs/lvm.py b/blivet/devicelibs/lvm.py
0b05ab
index 8eea9d19..65dc425e 100644
0b05ab
--- a/blivet/devicelibs/lvm.py
0b05ab
+++ b/blivet/devicelibs/lvm.py
0b05ab
@@ -38,7 +38,9 @@ from . import raid
0b05ab
 from ..size import Size
0b05ab
 from ..i18n import N_
0b05ab
 from ..flags import flags
0b05ab
+from ..static_data import lvs_info
0b05ab
 from ..tasks import availability
0b05ab
+from ..util import run_program
0b05ab
 
0b05ab
 # some of lvm's defaults that we have no way to ask it for
0b05ab
 LVM_PE_START = Size("1 MiB")
0b05ab
@@ -187,6 +189,22 @@ def lvmetad_socket_exists():
0b05ab
     return os.path.exists(LVMETAD_SOCKET_PATH)
0b05ab
 
0b05ab
 
0b05ab
+def ensure_lv_is_writable(vg_name, lv_name):
0b05ab
+    lv_info = lvs_info.cache.get("%s-%s" % (vg_name, lv_name))
0b05ab
+    if lv_info is None:
0b05ab
+        return
0b05ab
+
0b05ab
+    if lv_info.attr[1] == 'w':
0b05ab
+        return
0b05ab
+
0b05ab
+    try:
0b05ab
+        rc = run_program(['lvchange', '-prw', "%s/%s" % (vg_name, lv_name)])
0b05ab
+    except OSError:
0b05ab
+        rc = -1
0b05ab
+
0b05ab
+    return rc == 0
0b05ab
+
0b05ab
+
0b05ab
 def is_lvm_name_valid(name):
0b05ab
     # No . or ..
0b05ab
     if name == '.' or name == '..':
0b05ab
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
0b05ab
index 58adf5cf..dbecc1e5 100644
0b05ab
--- a/blivet/devices/lvm.py
0b05ab
+++ b/blivet/devices/lvm.py
0b05ab
@@ -951,6 +951,10 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice):
0b05ab
         # set up the vg's pvs so lvm can remove the lv
0b05ab
         self.vg.setup_parents(orig=True)
0b05ab
 
0b05ab
+    def set_rw(self):
0b05ab
+        """ Run lvchange as needed to ensure the lv is not read-only. """
0b05ab
+        lvm.ensure_lv_is_writable(self.vg.name, self.lvname)
0b05ab
+
0b05ab
     @property
0b05ab
     def lvname(self):
0b05ab
         """ The LV's name (not including VG name). """
0b05ab
-- 
0b05ab
2.24.1
0b05ab