1a21aa
From 43c5a6ef094e5f333a6dd47c467a1516488e2097 Mon Sep 17 00:00:00 2001
1a21aa
From: Vojtech Trefny <vtrefny@redhat.com>
1a21aa
Date: Mon, 24 May 2021 13:35:39 +0200
1a21aa
Subject: [PATCH 1/7] Remove action device from LVM reject list
1a21aa
1a21aa
Because the device doesn't depend on itself the existing code
1a21aa
won't remove the device we are trying to modify from the list.
1a21aa
1a21aa
Resolves: rhbz#1955942
1a21aa
---
1a21aa
 blivet/actionlist.py | 1 +
1a21aa
 1 file changed, 1 insertion(+)
1a21aa
1a21aa
diff --git a/blivet/actionlist.py b/blivet/actionlist.py
1a21aa
index d03e32b9..2de3fed3 100644
1a21aa
--- a/blivet/actionlist.py
1a21aa
+++ b/blivet/actionlist.py
1a21aa
@@ -260,6 +260,7 @@ class ActionList(object):
1a21aa
             log.debug("action: %s", action)
1a21aa
 
1a21aa
             # Remove lvm filters for devices we are operating on
1a21aa
+            lvm.lvm_cc_removeFilterRejectRegexp(action.device.name)
1a21aa
             for device in (d for d in devices if d.depends_on(action.device)):
1a21aa
                 lvm.lvm_cc_removeFilterRejectRegexp(device.name)
1a21aa
 
1a21aa
-- 
1a21aa
2.31.1
1a21aa
1a21aa
1a21aa
From 2db8aa0aa6ea03c182f7e8e08cd1371ded13b71c Mon Sep 17 00:00:00 2001
1a21aa
From: Vojtech Trefny <vtrefny@redhat.com>
1a21aa
Date: Mon, 24 May 2021 14:49:12 +0200
1a21aa
Subject: [PATCH 2/7] Convert LVM filter lists to sets
1a21aa
1a21aa
To prevent devices being added multiple times and removed only
1a21aa
once.
1a21aa
1a21aa
Related: rhbz#1955942
1a21aa
---
1a21aa
 blivet/devicelibs/lvm.py | 12 ++++++------
1a21aa
 tests/devicetree_test.py |  6 +++---
1a21aa
 2 files changed, 9 insertions(+), 9 deletions(-)
1a21aa
1a21aa
diff --git a/blivet/devicelibs/lvm.py b/blivet/devicelibs/lvm.py
1a21aa
index 121797ce..9e396cca 100644
1a21aa
--- a/blivet/devicelibs/lvm.py
1a21aa
+++ b/blivet/devicelibs/lvm.py
1a21aa
@@ -72,8 +72,8 @@ safe_name_characters = "0-9a-zA-Z._-"
1a21aa
 # Theoretically we can handle all that can be handled with the LVM --config
1a21aa
 # argument.  For every time we call an lvm_cc (lvm compose config) funciton
1a21aa
 # we regenerate the config_args with all global info.
1a21aa
-config_args_data = {"filterRejects": [],    # regular expressions to reject.
1a21aa
-                    "filterAccepts": []}   # regexp to accept
1a21aa
+config_args_data = {"filterRejects": set(),    # regular expressions to reject.
1a21aa
+                    "filterAccepts": set()}    # regexp to accept
1a21aa
 
1a21aa
 
1a21aa
 def _set_global_config():
1a21aa
@@ -125,7 +125,7 @@ def needs_config_refresh(fn):
1a21aa
 def lvm_cc_addFilterRejectRegexp(regexp):
1a21aa
     """ Add a regular expression to the --config string."""
1a21aa
     log.debug("lvm filter: adding %s to the reject list", regexp)
1a21aa
-    config_args_data["filterRejects"].append(regexp)
1a21aa
+    config_args_data["filterRejects"].add(regexp)
1a21aa
 
1a21aa
 
1a21aa
 @needs_config_refresh
1a21aa
@@ -134,15 +134,15 @@ def lvm_cc_removeFilterRejectRegexp(regexp):
1a21aa
     log.debug("lvm filter: removing %s from the reject list", regexp)
1a21aa
     try:
1a21aa
         config_args_data["filterRejects"].remove(regexp)
1a21aa
-    except ValueError:
1a21aa
+    except KeyError:
1a21aa
         log.debug("%s wasn't in the reject list", regexp)
1a21aa
         return
1a21aa
 
1a21aa
 
1a21aa
 @needs_config_refresh
1a21aa
 def lvm_cc_resetFilter():
1a21aa
-    config_args_data["filterRejects"] = []
1a21aa
-    config_args_data["filterAccepts"] = []
1a21aa
+    config_args_data["filterRejects"] = set()
1a21aa
+    config_args_data["filterAccepts"] = set()
1a21aa
 
1a21aa
 
1a21aa
 def determine_parent_lv(internal_lv, lvs, lv_info):
1a21aa
diff --git a/tests/devicetree_test.py b/tests/devicetree_test.py
1a21aa
index d1f4d8f3..ef163c0a 100644
1a21aa
--- a/tests/devicetree_test.py
1a21aa
+++ b/tests/devicetree_test.py
1a21aa
@@ -125,7 +125,7 @@ class DeviceTreeTestCase(unittest.TestCase):
1a21aa
         dt.actions._actions.append(Mock(name="fake action"))
1a21aa
 
1a21aa
         lvm.lvm_cc_addFilterRejectRegexp("xxx")
1a21aa
-        lvm.config_args_data["filterAccepts"].append("yyy")
1a21aa
+        lvm.config_args_data["filterAccepts"].add("yyy")
1a21aa
 
1a21aa
         dt.ignored_disks.append(names[0])
1a21aa
         dt.exclusive_disks.append(names[1])
1a21aa
@@ -144,8 +144,8 @@ class DeviceTreeTestCase(unittest.TestCase):
1a21aa
 
1a21aa
         self.assertEqual(dt._hidden, empty_list)
1a21aa
 
1a21aa
-        self.assertEqual(lvm.config_args_data["filterAccepts"], empty_list)
1a21aa
-        self.assertEqual(lvm.config_args_data["filterRejects"], empty_list)
1a21aa
+        self.assertEqual(lvm.config_args_data["filterAccepts"], set())
1a21aa
+        self.assertEqual(lvm.config_args_data["filterRejects"], set())
1a21aa
 
1a21aa
         self.assertEqual(dt.exclusive_disks, empty_list)
1a21aa
         self.assertEqual(dt.ignored_disks, empty_list)
1a21aa
-- 
1a21aa
2.31.1
1a21aa
1a21aa
1a21aa
From e2540422945586ca45848a663e391a91b2fdd714 Mon Sep 17 00:00:00 2001
1a21aa
From: Vojtech Trefny <vtrefny@redhat.com>
1a21aa
Date: Tue, 27 Jul 2021 14:07:05 +0200
1a21aa
Subject: [PATCH 3/7] Switch LVM devices filter from "reject" to "accept" by
1a21aa
 default
1a21aa
1a21aa
We currently use the LVM reject filter to filter out hidden and
1a21aa
ignored devices, this commit changes the behaviour to reject all
1a21aa
devices by default and accept only physical volumes that are not
1a21aa
hidden or ignored. This is preparation for the switch from the
1a21aa
existing lvm.conf based filtering to the new devices file based
1a21aa
filtering introduced in LVM 2.03.12 which allows only listing
1a21aa
"accepted" devices. This allows us to support both the "old" and
1a21aa
"new" style filtering using the same code.
1a21aa
---
1a21aa
 blivet/actionlist.py                  |  5 +--
1a21aa
 blivet/devicelibs/lvm.py              | 62 +++++++++++----------------
1a21aa
 blivet/devices/lvm.py                 |  4 +-
1a21aa
 blivet/devicetree.py                  |  8 ++--
1a21aa
 blivet/formats/lvmpv.py               |  2 +
1a21aa
 blivet/populator/helpers/lvm.py       |  6 +++
1a21aa
 blivet/populator/helpers/partition.py |  8 ----
1a21aa
 blivet/populator/populator.py         |  4 +-
1a21aa
 tests/devicetree_test.py              | 37 ++++++++++++++--
1a21aa
 tests/populator_test.py               |  6 ++-
1a21aa
 10 files changed, 81 insertions(+), 61 deletions(-)
1a21aa
1a21aa
diff --git a/blivet/actionlist.py b/blivet/actionlist.py
1a21aa
index 2de3fed3..f3977401 100644
1a21aa
--- a/blivet/actionlist.py
1a21aa
+++ b/blivet/actionlist.py
1a21aa
@@ -259,10 +259,9 @@ class ActionList(object):
1a21aa
         for action in self._actions:
1a21aa
             log.debug("action: %s", action)
1a21aa
 
1a21aa
-            # Remove lvm filters for devices we are operating on
1a21aa
-            lvm.lvm_cc_removeFilterRejectRegexp(action.device.name)
1a21aa
             for device in (d for d in devices if d.depends_on(action.device)):
1a21aa
-                lvm.lvm_cc_removeFilterRejectRegexp(device.name)
1a21aa
+                if device.format.type == "lvmpv":
1a21aa
+                    lvm.lvm_devices_add(device.path)
1a21aa
 
1a21aa
     def _post_process(self, devices=None):
1a21aa
         """ Clean up relics from action queue execution. """
1a21aa
diff --git a/blivet/devicelibs/lvm.py b/blivet/devicelibs/lvm.py
1a21aa
index 9e396cca..96d037b8 100644
1a21aa
--- a/blivet/devicelibs/lvm.py
1a21aa
+++ b/blivet/devicelibs/lvm.py
1a21aa
@@ -67,40 +67,29 @@ LVMETAD_SOCKET_PATH = "/run/lvm/lvmetad.socket"
1a21aa
 
1a21aa
 safe_name_characters = "0-9a-zA-Z._-"
1a21aa
 
1a21aa
-# Start config_args handling code
1a21aa
-#
1a21aa
-# Theoretically we can handle all that can be handled with the LVM --config
1a21aa
-# argument.  For every time we call an lvm_cc (lvm compose config) funciton
1a21aa
-# we regenerate the config_args with all global info.
1a21aa
-config_args_data = {"filterRejects": set(),    # regular expressions to reject.
1a21aa
-                    "filterAccepts": set()}    # regexp to accept
1a21aa
+# list of devices that LVM is allowed to use
1a21aa
+# with LVM >= 2.0.13 we'll use this for the --devices option and when creating
1a21aa
+# the /etc/lvm/devices/system.devices file
1a21aa
+# with older versions of LVM we will use this for the --config based filtering
1a21aa
+_lvm_devices = set()
1a21aa
 
1a21aa
 
1a21aa
 def _set_global_config():
1a21aa
     """lvm command accepts lvm.conf type arguments preceded by --config. """
1a21aa
 
1a21aa
-    filter_string = ""
1a21aa
-    rejects = config_args_data["filterRejects"]
1a21aa
-    for reject in rejects:
1a21aa
-        filter_string += ("\"r|/%s$|\"," % reject)
1a21aa
+    device_string = ""
1a21aa
+
1a21aa
+    # now explicitly "accept" all LVM devices
1a21aa
+    for device in _lvm_devices:
1a21aa
+        device_string += "\"a|%s$|\"," % device
1a21aa
 
1a21aa
-    if filter_string:
1a21aa
-        filter_string = "filter=[%s]" % filter_string.strip(",")
1a21aa
+    # now add all devices to the "reject" filter
1a21aa
+    device_string += "\"r|.*|\""
1a21aa
 
1a21aa
-    # XXX consider making /tmp/blivet.lvm.XXXXX, writing an lvm.conf there, and
1a21aa
-    #     setting LVM_SYSTEM_DIR
1a21aa
-    devices_string = 'preferred_names=["^/dev/mapper/", "^/dev/md/", "^/dev/sd"]'
1a21aa
-    if filter_string:
1a21aa
-        devices_string += " %s" % filter_string
1a21aa
+    filter_string = "filter=[%s]" % device_string
1a21aa
 
1a21aa
-    # for now ignore the LVM devices file and rely on our filters
1a21aa
-    if availability.LVMDEVICES.available:
1a21aa
-        devices_string += " use_devicesfile=0"
1a21aa
+    config_string = " devices { %s } " % filter_string
1a21aa
 
1a21aa
-    # devices_string can have (inside the brackets) "dir", "scan",
1a21aa
-    # "preferred_names", "filter", "cache_dir", "write_cache_state",
1a21aa
-    # "types", "sysfs_scan", "md_component_detection".  see man lvm.conf.
1a21aa
-    config_string = " devices { %s } " % (devices_string)  # strings can be added
1a21aa
     if not flags.lvm_metadata_backup:
1a21aa
         config_string += "backup {backup=0 archive=0} "
1a21aa
     if flags.debug:
1a21aa
@@ -122,27 +111,26 @@ def needs_config_refresh(fn):
1a21aa
 
1a21aa
 
1a21aa
 @needs_config_refresh
1a21aa
-def lvm_cc_addFilterRejectRegexp(regexp):
1a21aa
-    """ Add a regular expression to the --config string."""
1a21aa
-    log.debug("lvm filter: adding %s to the reject list", regexp)
1a21aa
-    config_args_data["filterRejects"].add(regexp)
1a21aa
+def lvm_devices_add(path):
1a21aa
+    """ Add a device (PV) to the list of devices LVM is allowed to use """
1a21aa
+    log.debug("lvm filter: device %s added to the list of allowed devices")
1a21aa
+    _lvm_devices.add(path)
1a21aa
 
1a21aa
 
1a21aa
 @needs_config_refresh
1a21aa
-def lvm_cc_removeFilterRejectRegexp(regexp):
1a21aa
-    """ Remove a regular expression from the --config string."""
1a21aa
-    log.debug("lvm filter: removing %s from the reject list", regexp)
1a21aa
+def lvm_devices_remove(path):
1a21aa
+    """ Remove a device (PV) to the list of devices LVM is allowed to use """
1a21aa
+    log.debug("lvm filter: device %s removed from the list of allowed devices")
1a21aa
     try:
1a21aa
-        config_args_data["filterRejects"].remove(regexp)
1a21aa
+        _lvm_devices.remove(path)
1a21aa
     except KeyError:
1a21aa
-        log.debug("%s wasn't in the reject list", regexp)
1a21aa
+        log.debug("%s wasn't in the devices list", path)
1a21aa
         return
1a21aa
 
1a21aa
 
1a21aa
 @needs_config_refresh
1a21aa
-def lvm_cc_resetFilter():
1a21aa
-    config_args_data["filterRejects"] = set()
1a21aa
-    config_args_data["filterAccepts"] = set()
1a21aa
+def lvm_devices_reset():
1a21aa
+    _lvm_devices.clear()
1a21aa
 
1a21aa
 
1a21aa
 def determine_parent_lv(internal_lv, lvs, lv_info):
1a21aa
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
1a21aa
index c61eeb4b..9c230f1b 100644
1a21aa
--- a/blivet/devices/lvm.py
1a21aa
+++ b/blivet/devices/lvm.py
1a21aa
@@ -273,8 +273,8 @@ class LVMVolumeGroupDevice(ContainerDevice):
1a21aa
         log_method_call(self, self.name, status=self.status)
1a21aa
         if not self.complete:
1a21aa
             for pv in self.pvs:
1a21aa
-                # Remove the PVs from the ignore filter so we can wipe them.
1a21aa
-                lvm.lvm_cc_removeFilterRejectRegexp(pv.name)
1a21aa
+                # add PVS to the list of LVM devices so we can wipe them.
1a21aa
+                lvm.lvm_devices_add(pv.path)
1a21aa
 
1a21aa
             # Don't run vgremove or vgreduce since there may be another VG with
1a21aa
             # the same name that we want to keep/use.
1a21aa
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
1a21aa
index f4ae1968..c6c1b440 100644
1a21aa
--- a/blivet/devicetree.py
1a21aa
+++ b/blivet/devicetree.py
1a21aa
@@ -96,7 +96,7 @@ class DeviceTreeBase(object):
1a21aa
 
1a21aa
         self._hidden = []
1a21aa
 
1a21aa
-        lvm.lvm_cc_resetFilter()
1a21aa
+        lvm.lvm_devices_reset()
1a21aa
 
1a21aa
         self.exclusive_disks = exclusive_disks or []
1a21aa
         self.ignored_disks = ignored_disks or []
1a21aa
@@ -879,7 +879,8 @@ class DeviceTreeBase(object):
1a21aa
         self._remove_device(device, force=True, modparent=False)
1a21aa
 
1a21aa
         self._hidden.append(device)
1a21aa
-        lvm.lvm_cc_addFilterRejectRegexp(device.name)
1a21aa
+        if device.format.type == "lvmpv":
1a21aa
+            lvm.lvm_devices_remove(device.path)
1a21aa
 
1a21aa
     def unhide(self, device):
1a21aa
         """ Restore a device's visibility.
1a21aa
@@ -905,7 +906,8 @@ class DeviceTreeBase(object):
1a21aa
                 self._hidden.remove(hidden)
1a21aa
                 self._devices.append(hidden)
1a21aa
                 hidden.add_hook(new=False)
1a21aa
-                lvm.lvm_cc_removeFilterRejectRegexp(hidden.name)
1a21aa
+                if hidden.format.type == "lvmpv":
1a21aa
+                    lvm.lvm_devices_add(hidden.path)
1a21aa
 
1a21aa
     def expand_taglist(self, taglist):
1a21aa
         """ Expands tags in input list into devices.
1a21aa
diff --git a/blivet/formats/lvmpv.py b/blivet/formats/lvmpv.py
1a21aa
index ea84e9e4..3b00951f 100644
1a21aa
--- a/blivet/formats/lvmpv.py
1a21aa
+++ b/blivet/formats/lvmpv.py
1a21aa
@@ -124,6 +124,7 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
     def _create(self, **kwargs):
1a21aa
         log_method_call(self, device=self.device,
1a21aa
                         type=self.type, status=self.status)
1a21aa
+        lvm.lvm_devices_add(self.device)
1a21aa
 
1a21aa
         lvm._set_global_config()
1a21aa
 
1a21aa
@@ -138,6 +139,7 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
         except blockdev.LVMError:
1a21aa
             DeviceFormat._destroy(self, **kwargs)
1a21aa
         finally:
1a21aa
+            lvm.lvm_devices_remove(self.device)
1a21aa
             udev.settle()
1a21aa
 
1a21aa
     @property
1a21aa
diff --git a/blivet/populator/helpers/lvm.py b/blivet/populator/helpers/lvm.py
1a21aa
index c7adfa4e..9e7e4630 100644
1a21aa
--- a/blivet/populator/helpers/lvm.py
1a21aa
+++ b/blivet/populator/helpers/lvm.py
1a21aa
@@ -87,6 +87,12 @@ class LVMFormatPopulator(FormatPopulator):
1a21aa
     def _get_kwargs(self):
1a21aa
         kwargs = super(LVMFormatPopulator, self)._get_kwargs()
1a21aa
 
1a21aa
+        # new PV, add it to the LVM devices list and re-run pvs/lvs/vgs
1a21aa
+        lvm.lvm_devices_add(self.device.path)
1a21aa
+        pvs_info.drop_cache()
1a21aa
+        vgs_info.drop_cache()
1a21aa
+        lvs_info.drop_cache()
1a21aa
+
1a21aa
         pv_info = pvs_info.cache.get(self.device.path, None)
1a21aa
 
1a21aa
         name = udev.device_get_name(self.data)
1a21aa
diff --git a/blivet/populator/helpers/partition.py b/blivet/populator/helpers/partition.py
1a21aa
index f00323d1..8659bd48 100644
1a21aa
--- a/blivet/populator/helpers/partition.py
1a21aa
+++ b/blivet/populator/helpers/partition.py
1a21aa
@@ -24,7 +24,6 @@ import copy
1a21aa
 import six
1a21aa
 
1a21aa
 from ... import udev
1a21aa
-from ...devicelibs import lvm
1a21aa
 from ...devices import PartitionDevice
1a21aa
 from ...errors import DeviceError
1a21aa
 from ...formats import get_format
1a21aa
@@ -66,7 +65,6 @@ class PartitionDevicePopulator(DevicePopulator):
1a21aa
         if disk is None:
1a21aa
             # if the disk is still not in the tree something has gone wrong
1a21aa
             log.error("failure finding disk for %s", name)
1a21aa
-            lvm.lvm_cc_addFilterRejectRegexp(name)
1a21aa
             return
1a21aa
 
1a21aa
         if not disk.partitioned or not disk.format.supported:
1a21aa
@@ -78,12 +76,6 @@ class PartitionDevicePopulator(DevicePopulator):
1a21aa
             # and instantiate a PartitionDevice so our view of the layout is
1a21aa
             # complete.
1a21aa
             if not disk.partitionable or disk.format.type == "iso9660" or disk.format.hidden:
1a21aa
-                # there's no need to filter partitions on members of multipaths or
1a21aa
-                # fwraid members from lvm since multipath and dmraid are already
1a21aa
-                # active and lvm should therefore know to ignore them
1a21aa
-                if not disk.format.hidden:
1a21aa
-                    lvm.lvm_cc_addFilterRejectRegexp(name)
1a21aa
-
1a21aa
                 log.debug("ignoring partition %s on %s", name, disk.format.type)
1a21aa
                 return
1a21aa
 
1a21aa
diff --git a/blivet/populator/populator.py b/blivet/populator/populator.py
1a21aa
index 75bb1741..958593ec 100644
1a21aa
--- a/blivet/populator/populator.py
1a21aa
+++ b/blivet/populator/populator.py
1a21aa
@@ -317,10 +317,10 @@ class PopulatorMixin(object):
1a21aa
                 continue
1a21aa
 
1a21aa
             # Make sure lvm doesn't get confused by PVs that belong to
1a21aa
-            # incomplete VGs. We will remove the PVs from the reject list when/if
1a21aa
+            # incomplete VGs. We will add the PVs to the accept list when/if
1a21aa
             # the time comes to remove the incomplete VG and its PVs.
1a21aa
             for pv in vg.pvs:
1a21aa
-                lvm.lvm_cc_addFilterRejectRegexp(pv.name)
1a21aa
+                lvm.lvm_devices_remove(pv.path)
1a21aa
 
1a21aa
     def set_disk_images(self, images):
1a21aa
         """ Set the disk images and reflect them in exclusive_disks.
1a21aa
diff --git a/tests/devicetree_test.py b/tests/devicetree_test.py
1a21aa
index ef163c0a..3be4d572 100644
1a21aa
--- a/tests/devicetree_test.py
1a21aa
+++ b/tests/devicetree_test.py
1a21aa
@@ -124,8 +124,7 @@ class DeviceTreeTestCase(unittest.TestCase):
1a21aa
 
1a21aa
         dt.actions._actions.append(Mock(name="fake action"))
1a21aa
 
1a21aa
-        lvm.lvm_cc_addFilterRejectRegexp("xxx")
1a21aa
-        lvm.config_args_data["filterAccepts"].add("yyy")
1a21aa
+        lvm.lvm_devices_add("xxx")
1a21aa
 
1a21aa
         dt.ignored_disks.append(names[0])
1a21aa
         dt.exclusive_disks.append(names[1])
1a21aa
@@ -144,8 +143,7 @@ class DeviceTreeTestCase(unittest.TestCase):
1a21aa
 
1a21aa
         self.assertEqual(dt._hidden, empty_list)
1a21aa
 
1a21aa
-        self.assertEqual(lvm.config_args_data["filterAccepts"], set())
1a21aa
-        self.assertEqual(lvm.config_args_data["filterRejects"], set())
1a21aa
+        self.assertEqual(lvm._lvm_devices, set())
1a21aa
 
1a21aa
         self.assertEqual(dt.exclusive_disks, empty_list)
1a21aa
         self.assertEqual(dt.ignored_disks, empty_list)
1a21aa
@@ -438,6 +436,37 @@ class DeviceTreeTestCase(unittest.TestCase):
1a21aa
         self.assertEqual(tree.get_related_disks(sda), set([sda, sdb]))
1a21aa
         self.assertEqual(tree.get_related_disks(sdb), set([sda, sdb]))
1a21aa
 
1a21aa
+    def test_lvm_filter_hide_unhide(self):
1a21aa
+        tree = DeviceTree()
1a21aa
+
1a21aa
+        sda = DiskDevice("sda", size=Size("30 GiB"))
1a21aa
+        sdb = DiskDevice("sdb", size=Size("30 GiB"))
1a21aa
+
1a21aa
+        tree._add_device(sda)
1a21aa
+        tree._add_device(sdb)
1a21aa
+
1a21aa
+        self.assertTrue(sda in tree.devices)
1a21aa
+        self.assertTrue(sdb in tree.devices)
1a21aa
+
1a21aa
+        sda.format = get_format("lvmpv", device=sda.path)
1a21aa
+        sdb.format = get_format("lvmpv", device=sdb.path)
1a21aa
+
1a21aa
+        # LVMPhysicalVolume._create would do this
1a21aa
+        lvm.lvm_devices_add(sda.path)
1a21aa
+        lvm.lvm_devices_add(sdb.path)
1a21aa
+
1a21aa
+        self.assertSetEqual(lvm._lvm_devices, {sda.path, sdb.path})
1a21aa
+
1a21aa
+        tree.hide(sda)
1a21aa
+        self.assertSetEqual(lvm._lvm_devices, {sdb.path})
1a21aa
+        tree.hide(sdb)
1a21aa
+        self.assertSetEqual(lvm._lvm_devices, set())
1a21aa
+
1a21aa
+        tree.unhide(sda)
1a21aa
+        self.assertSetEqual(lvm._lvm_devices, {sda.path})
1a21aa
+        tree.unhide(sdb)
1a21aa
+        self.assertSetEqual(lvm._lvm_devices, {sda.path, sdb.path})
1a21aa
+
1a21aa
 
1a21aa
 class DeviceTreeIgnoredExclusiveMultipathTestCase(unittest.TestCase):
1a21aa
 
1a21aa
diff --git a/tests/populator_test.py b/tests/populator_test.py
1a21aa
index 2a8532f0..dd36c16a 100644
1a21aa
--- a/tests/populator_test.py
1a21aa
+++ b/tests/populator_test.py
1a21aa
@@ -13,6 +13,7 @@ from gi.repository import BlockDev as blockdev
1a21aa
 from blivet.devices import DiskDevice, DMDevice, FileDevice, LoopDevice
1a21aa
 from blivet.devices import MDRaidArrayDevice, MultipathDevice, OpticalDevice
1a21aa
 from blivet.devices import PartitionDevice, StorageDevice, NVDIMMNamespaceDevice
1a21aa
+from blivet.devicelibs import lvm
1a21aa
 from blivet.devicetree import DeviceTree
1a21aa
 from blivet.formats import get_device_format_class, get_format, DeviceFormat
1a21aa
 from blivet.formats.disklabel import DiskLabel
1a21aa
@@ -393,8 +394,7 @@ class PartitionDevicePopulatorTestCase(PopulatorHelperTestCase):
1a21aa
     @patch.object(DiskLabel, "parted_disk")
1a21aa
     @patch.object(DiskLabel, "parted_device")
1a21aa
     @patch.object(PartitionDevice, "probe")
1a21aa
-    # TODO: fix the naming of the lvm filter functions
1a21aa
-    @patch("blivet.devicelibs.lvm.lvm_cc_addFilterRejectRegexp")
1a21aa
+    @patch("blivet.devicelibs.lvm.lvm_devices_add")
1a21aa
     @patch("blivet.udev.device_get_major", return_value=88)
1a21aa
     @patch("blivet.udev.device_get_minor", return_value=19)
1a21aa
     @patch.object(DeviceTree, "get_device_by_name")
1a21aa
@@ -973,6 +973,8 @@ class LVMFormatPopulatorTestCase(FormatPopulatorTestCase):
1a21aa
                     self.assertTrue(vg_device is not None)
1a21aa
                     devicetree._remove_device(vg_device)
1a21aa
 
1a21aa
+                    self.assertIn(device.path, lvm._lvm_devices)
1a21aa
+
1a21aa
         get_device_by_uuid.reset_mock()
1a21aa
 
1a21aa
         # pv belongs to a valid vg not in the tree with two lvs
1a21aa
-- 
1a21aa
2.31.1
1a21aa
1a21aa
1a21aa
From 15a63b01bd2b6e7fe197fade849f28b83407c166 Mon Sep 17 00:00:00 2001
1a21aa
From: Vojtech Trefny <vtrefny@redhat.com>
1a21aa
Date: Fri, 30 Jul 2021 14:01:04 +0200
1a21aa
Subject: [PATCH 4/7] Use LVM devices for filtering LVM devices with LVM >=
1a21aa
 2.02.13
1a21aa
1a21aa
---
1a21aa
 blivet/devicelibs/lvm.py | 38 +++++++++++++++++++++++++++++---------
1a21aa
 tests/populator_test.py  |  9 ++++-----
1a21aa
 2 files changed, 33 insertions(+), 14 deletions(-)
1a21aa
1a21aa
diff --git a/blivet/devicelibs/lvm.py b/blivet/devicelibs/lvm.py
1a21aa
index 96d037b8..3ab1540b 100644
1a21aa
--- a/blivet/devicelibs/lvm.py
1a21aa
+++ b/blivet/devicelibs/lvm.py
1a21aa
@@ -67,6 +67,16 @@ LVMETAD_SOCKET_PATH = "/run/lvm/lvmetad.socket"
1a21aa
 
1a21aa
 safe_name_characters = "0-9a-zA-Z._-"
1a21aa
 
1a21aa
+if hasattr(blockdev.LVMTech, "DEVICES"):
1a21aa
+    try:
1a21aa
+        blockdev.lvm.is_tech_avail(blockdev.LVMTech.DEVICES, 0)  # pylint: disable=no-member
1a21aa
+    except blockdev.LVMError:
1a21aa
+        HAVE_LVMDEVICES = False
1a21aa
+    else:
1a21aa
+        HAVE_LVMDEVICES = True
1a21aa
+else:
1a21aa
+    HAVE_LVMDEVICES = False
1a21aa
+
1a21aa
 # list of devices that LVM is allowed to use
1a21aa
 # with LVM >= 2.0.13 we'll use this for the --devices option and when creating
1a21aa
 # the /etc/lvm/devices/system.devices file
1a21aa
@@ -79,25 +89,34 @@ def _set_global_config():
1a21aa
 
1a21aa
     device_string = ""
1a21aa
 
1a21aa
-    # now explicitly "accept" all LVM devices
1a21aa
-    for device in _lvm_devices:
1a21aa
-        device_string += "\"a|%s$|\"," % device
1a21aa
+    if not HAVE_LVMDEVICES:
1a21aa
+        # now explicitly "accept" all LVM devices
1a21aa
+        for device in _lvm_devices:
1a21aa
+            device_string += "\"a|%s$|\"," % device
1a21aa
 
1a21aa
-    # now add all devices to the "reject" filter
1a21aa
-    device_string += "\"r|.*|\""
1a21aa
+        # now add all devices to the "reject" filter
1a21aa
+        device_string += "\"r|.*|\""
1a21aa
 
1a21aa
-    filter_string = "filter=[%s]" % device_string
1a21aa
+        filter_string = "filter=[%s]" % device_string
1a21aa
 
1a21aa
-    config_string = " devices { %s } " % filter_string
1a21aa
+        config_string = " devices { %s } " % filter_string
1a21aa
+    else:
1a21aa
+        config_string = " "
1a21aa
 
1a21aa
     if not flags.lvm_metadata_backup:
1a21aa
         config_string += "backup {backup=0 archive=0} "
1a21aa
-    if flags.debug:
1a21aa
-        config_string += "log {level=7 file=/tmp/lvm.log syslog=0}"
1a21aa
+    config_string += "log {level=7 file=/tmp/lvm.log syslog=0}"
1a21aa
 
1a21aa
     blockdev.lvm.set_global_config(config_string)
1a21aa
 
1a21aa
 
1a21aa
+def _set_lvm_devices():
1a21aa
+    if not HAVE_LVMDEVICES:
1a21aa
+        return
1a21aa
+
1a21aa
+    blockdev.lvm.set_devices_filter(list(_lvm_devices))
1a21aa
+
1a21aa
+
1a21aa
 def needs_config_refresh(fn):
1a21aa
     if not availability.BLOCKDEV_LVM_PLUGIN.available:
1a21aa
         return lambda *args, **kwargs: None
1a21aa
@@ -105,6 +124,7 @@ def needs_config_refresh(fn):
1a21aa
     def fn_with_refresh(*args, **kwargs):
1a21aa
         ret = fn(*args, **kwargs)
1a21aa
         _set_global_config()
1a21aa
+        _set_lvm_devices()
1a21aa
         return ret
1a21aa
 
1a21aa
     return fn_with_refresh
1a21aa
diff --git a/tests/populator_test.py b/tests/populator_test.py
1a21aa
index dd36c16a..a9584319 100644
1a21aa
--- a/tests/populator_test.py
1a21aa
+++ b/tests/populator_test.py
1a21aa
@@ -897,6 +897,7 @@ class LVMFormatPopulatorTestCase(FormatPopulatorTestCase):
1a21aa
         device = Mock()
1a21aa
         device.parents = []
1a21aa
         device.size = Size("10g")
1a21aa
+        device.path = "/dev/sda1"
1a21aa
         devicetree._add_device(device)
1a21aa
 
1a21aa
         # pylint: disable=attribute-defined-outside-init
1a21aa
@@ -924,15 +925,13 @@ class LVMFormatPopulatorTestCase(FormatPopulatorTestCase):
1a21aa
         pv_info.pe_start = 0
1a21aa
         pv_info.pv_free = 0
1a21aa
 
1a21aa
-        device.path = sentinel.pv_path
1a21aa
-
1a21aa
         vg_device = Mock()
1a21aa
         vg_device.parents = []
1a21aa
         vg_device.lvs = []
1a21aa
         get_device_by_uuid.return_value = vg_device
1a21aa
 
1a21aa
         with patch("blivet.static_data.lvm_info.PVsInfo.cache", new_callable=PropertyMock) as mock_pvs_cache:
1a21aa
-            mock_pvs_cache.return_value = {sentinel.pv_path: pv_info}
1a21aa
+            mock_pvs_cache.return_value = {device.path: pv_info}
1a21aa
             with patch("blivet.udev.device_get_format", return_value=self.udev_type):
1a21aa
                 helper = self.helper_class(devicetree, data, device)
1a21aa
                 self.assertFalse(device in vg_device.parents)
1a21aa
@@ -957,7 +956,7 @@ class LVMFormatPopulatorTestCase(FormatPopulatorTestCase):
1a21aa
         pv_info.vg_pv_count = 1
1a21aa
 
1a21aa
         with patch("blivet.static_data.lvm_info.PVsInfo.cache", new_callable=PropertyMock) as mock_pvs_cache:
1a21aa
-            mock_pvs_cache.return_value = {sentinel.pv_path: pv_info}
1a21aa
+            mock_pvs_cache.return_value = {device.path: pv_info}
1a21aa
             with patch("blivet.static_data.lvm_info.VGsInfo.cache", new_callable=PropertyMock) as mock_vgs_cache:
1a21aa
                 mock_vgs_cache.return_value = {pv_info.vg_uuid: Mock()}
1a21aa
                 with patch("blivet.udev.device_get_format", return_value=self.udev_type):
1a21aa
@@ -1007,7 +1006,7 @@ class LVMFormatPopulatorTestCase(FormatPopulatorTestCase):
1a21aa
         get_device_by_uuid.side_effect = gdbu
1a21aa
 
1a21aa
         with patch("blivet.static_data.lvm_info.PVsInfo.cache", new_callable=PropertyMock) as mock_pvs_cache:
1a21aa
-            mock_pvs_cache.return_value = {sentinel.pv_path: pv_info}
1a21aa
+            mock_pvs_cache.return_value = {device.path: pv_info}
1a21aa
             with patch("blivet.static_data.lvm_info.VGsInfo.cache", new_callable=PropertyMock) as mock_vgs_cache:
1a21aa
                 mock_vgs_cache.return_value = {pv_info.vg_uuid: Mock()}
1a21aa
                 with patch("blivet.static_data.lvm_info.LVsInfo.cache", new_callable=PropertyMock) as mock_lvs_cache:
1a21aa
-- 
1a21aa
2.31.1
1a21aa
1a21aa
1a21aa
From d4e1395de3691f30196b6b0e3b2c82e83b27afaf Mon Sep 17 00:00:00 2001
1a21aa
From: Vojtech Trefny <vtrefny@redhat.com>
1a21aa
Date: Fri, 30 Jul 2021 14:01:43 +0200
1a21aa
Subject: [PATCH 5/7] Make sure PVs are added/deleted to/from the LVM device
1a21aa
 file
1a21aa
1a21aa
We are using the --devices option when running LVM commands which
1a21aa
mean the newly created PV won't be added to the device list by
1a21aa
pvcreate so we need to do that manually.
1a21aa
---
1a21aa
 blivet/formats/lvmpv.py | 5 +++++
1a21aa
 1 file changed, 5 insertions(+)
1a21aa
1a21aa
diff --git a/blivet/formats/lvmpv.py b/blivet/formats/lvmpv.py
1a21aa
index 3b00951f..71ec699f 100644
1a21aa
--- a/blivet/formats/lvmpv.py
1a21aa
+++ b/blivet/formats/lvmpv.py
1a21aa
@@ -131,6 +131,9 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
         ea_yes = blockdev.ExtraArg.new("-y", "")
1a21aa
         blockdev.lvm.pvcreate(self.device, data_alignment=self.data_alignment, extra=[ea_yes])
1a21aa
 
1a21aa
+        if lvm.HAVE_LVMDEVICES:
1a21aa
+            blockdev.lvm.devices_add(self.device)
1a21aa
+
1a21aa
     def _destroy(self, **kwargs):
1a21aa
         log_method_call(self, device=self.device,
1a21aa
                         type=self.type, status=self.status)
1a21aa
@@ -141,6 +144,8 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
         finally:
1a21aa
             lvm.lvm_devices_remove(self.device)
1a21aa
             udev.settle()
1a21aa
+            if lvm.HAVE_LVMDEVICES:
1a21aa
+                blockdev.lvm.devices_delete(self.device)
1a21aa
 
1a21aa
     @property
1a21aa
     def destroyable(self):
1a21aa
-- 
1a21aa
2.31.1
1a21aa
1a21aa
1a21aa
From c221d313bde21fb2cba701b93fe0c57336cba8ec Mon Sep 17 00:00:00 2001
1a21aa
From: Vojtech Trefny <vtrefny@redhat.com>
1a21aa
Date: Thu, 14 Oct 2021 15:32:24 +0200
1a21aa
Subject: [PATCH 6/7] Ignore errors for LVM devices file actions
1a21aa
1a21aa
The LVM devices file feature might be disabled either locally or
1a21aa
globally by LVM config.
1a21aa
---
1a21aa
 blivet/formats/lvmpv.py | 10 ++++++++--
1a21aa
 1 file changed, 8 insertions(+), 2 deletions(-)
1a21aa
1a21aa
diff --git a/blivet/formats/lvmpv.py b/blivet/formats/lvmpv.py
1a21aa
index 71ec699f..b27213cc 100644
1a21aa
--- a/blivet/formats/lvmpv.py
1a21aa
+++ b/blivet/formats/lvmpv.py
1a21aa
@@ -132,7 +132,10 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
         blockdev.lvm.pvcreate(self.device, data_alignment=self.data_alignment, extra=[ea_yes])
1a21aa
 
1a21aa
         if lvm.HAVE_LVMDEVICES:
1a21aa
-            blockdev.lvm.devices_add(self.device)
1a21aa
+            try:
1a21aa
+                blockdev.lvm.devices_add(self.device)
1a21aa
+            except blockdev.LVMError as e:
1a21aa
+                log.debug("Failed to add newly created PV %s to the LVM devices file: %s", self.device, str(e))
1a21aa
 
1a21aa
     def _destroy(self, **kwargs):
1a21aa
         log_method_call(self, device=self.device,
1a21aa
@@ -145,7 +148,10 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
             lvm.lvm_devices_remove(self.device)
1a21aa
             udev.settle()
1a21aa
             if lvm.HAVE_LVMDEVICES:
1a21aa
-                blockdev.lvm.devices_delete(self.device)
1a21aa
+                try:
1a21aa
+                    blockdev.lvm.devices_delete(self.device)
1a21aa
+                except blockdev.LVMError as e:
1a21aa
+                    log.debug("Failed to remove PV %s from the LVM devices file: %s", self.device, str(e))
1a21aa
 
1a21aa
     @property
1a21aa
     def destroyable(self):
1a21aa
-- 
1a21aa
2.31.1
1a21aa
1a21aa
1a21aa
From 6b96d4ead6890fffd95840b8935f71ecd9e310ef Mon Sep 17 00:00:00 2001
1a21aa
From: Vojtech Trefny <vtrefny@redhat.com>
1a21aa
Date: Tue, 19 Oct 2021 14:27:05 +0200
1a21aa
Subject: [PATCH 7/7] Add public functions to add/remove PV to/from the LVM
1a21aa
 system.devices
1a21aa
1a21aa
Anaconda needs to be able to add preexisting PVs to the file
1a21aa
during installation.
1a21aa
---
1a21aa
 blivet/formats/lvmpv.py | 28 ++++++++++++++++++++--------
1a21aa
 1 file changed, 20 insertions(+), 8 deletions(-)
1a21aa
1a21aa
diff --git a/blivet/formats/lvmpv.py b/blivet/formats/lvmpv.py
1a21aa
index b27213cc..3fef667e 100644
1a21aa
--- a/blivet/formats/lvmpv.py
1a21aa
+++ b/blivet/formats/lvmpv.py
1a21aa
@@ -121,6 +121,24 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
     def supported(self):
1a21aa
         return super(LVMPhysicalVolume, self).supported and self._plugin.available
1a21aa
 
1a21aa
+    def lvmdevices_add(self):
1a21aa
+        if not lvm.HAVE_LVMDEVICES:
1a21aa
+            raise PhysicalVolumeError("LVM devices file feature is not supported")
1a21aa
+
1a21aa
+        try:
1a21aa
+            blockdev.lvm.devices_add(self.device)
1a21aa
+        except blockdev.LVMError as e:
1a21aa
+            log.debug("Failed to add PV %s to the LVM devices file: %s", self.device, str(e))
1a21aa
+
1a21aa
+    def lvmdevices_remove(self):
1a21aa
+        if not lvm.HAVE_LVMDEVICES:
1a21aa
+            raise PhysicalVolumeError("LVM devices file feature is not supported")
1a21aa
+
1a21aa
+        try:
1a21aa
+            blockdev.lvm.devices_delete(self.device)
1a21aa
+        except blockdev.LVMError as e:
1a21aa
+            log.debug("Failed to remove PV %s from the LVM devices file: %s", self.device, str(e))
1a21aa
+
1a21aa
     def _create(self, **kwargs):
1a21aa
         log_method_call(self, device=self.device,
1a21aa
                         type=self.type, status=self.status)
1a21aa
@@ -132,10 +150,7 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
         blockdev.lvm.pvcreate(self.device, data_alignment=self.data_alignment, extra=[ea_yes])
1a21aa
 
1a21aa
         if lvm.HAVE_LVMDEVICES:
1a21aa
-            try:
1a21aa
-                blockdev.lvm.devices_add(self.device)
1a21aa
-            except blockdev.LVMError as e:
1a21aa
-                log.debug("Failed to add newly created PV %s to the LVM devices file: %s", self.device, str(e))
1a21aa
+            self.lvmdevices_add()
1a21aa
 
1a21aa
     def _destroy(self, **kwargs):
1a21aa
         log_method_call(self, device=self.device,
1a21aa
@@ -148,10 +163,7 @@ class LVMPhysicalVolume(DeviceFormat):
1a21aa
             lvm.lvm_devices_remove(self.device)
1a21aa
             udev.settle()
1a21aa
             if lvm.HAVE_LVMDEVICES:
1a21aa
-                try:
1a21aa
-                    blockdev.lvm.devices_delete(self.device)
1a21aa
-                except blockdev.LVMError as e:
1a21aa
-                    log.debug("Failed to remove PV %s from the LVM devices file: %s", self.device, str(e))
1a21aa
+                self.lvmdevices_remove()
1a21aa
 
1a21aa
     @property
1a21aa
     def destroyable(self):
1a21aa
-- 
1a21aa
2.31.1
1a21aa