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

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