Blame SOURCES/0011-Fix-ignoring-disk-devices-with-parents-or-children.patch

35c42d
From f19140993e94be9e58c8a01c18f1907792f59927 Mon Sep 17 00:00:00 2001
35c42d
From: Vojtech Trefny <vtrefny@redhat.com>
35c42d
Date: Wed, 5 Aug 2020 13:44:38 +0200
35c42d
Subject: [PATCH] Fix ignoring disk devices with parents or children
35c42d
35c42d
For disk-like devices like multipath we should allow to ignore
35c42d
these by simply ignoring the mpath device or by ignoring all of its
35c42d
drives.
35c42d
35c42d
- when ignoring the "mpatha" device we should also ignore "sda" and
35c42d
"sdb"
35c42d
- when ignoring both "sda" and "sdb" we should also ignore "mpatha"
35c42d
- when ignoring only "sda" we should not ignore "mpatha" (we don't
35c42d
want to deal with an "incomplete" multipath device in the tree)
35c42d
35c42d
This is consistent with the existing behaviour when using exclusive
35c42d
disks (or "ignoredisks --only-use" in kickstart).
35c42d
35c42d
Resolves: rhbz#1866243
35c42d
---
35c42d
 blivet/devicetree.py     |  51 ++++++++-----
35c42d
 tests/devicetree_test.py | 157 ++++++++++++++++++++++++++++-----------
35c42d
 2 files changed, 146 insertions(+), 62 deletions(-)
35c42d
35c42d
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
35c42d
index 5cc360e1..2afb0d0e 100644
35c42d
--- a/blivet/devicetree.py
35c42d
+++ b/blivet/devicetree.py
35c42d
@@ -907,31 +907,48 @@ class DeviceTreeBase(object):
35c42d
                 hidden.add_hook(new=False)
35c42d
                 lvm.lvm_cc_removeFilterRejectRegexp(hidden.name)
35c42d
 
35c42d
+    def _disk_in_taglist(self, disk, taglist):
35c42d
+        # Taglist is a list containing mix of disk names and tags into which disk may belong.
35c42d
+        # Check if it does. Raise ValueError if unknown tag is encountered.
35c42d
+        if disk.name in taglist:
35c42d
+            return True
35c42d
+        tags = [t[1:] for t in taglist if t.startswith("@")]
35c42d
+        for tag in tags:
35c42d
+            if tag not in Tags.__members__:
35c42d
+                raise ValueError("unknown ignoredisk tag '@%s' encountered" % tag)
35c42d
+            if Tags(tag) in disk.tags:
35c42d
+                return True
35c42d
+        return False
35c42d
+
35c42d
     def _is_ignored_disk(self, disk):
35c42d
         """ Checks config for lists of exclusive and ignored disks
35c42d
             and returns if the given one should be ignored
35c42d
         """
35c42d
-
35c42d
-        def disk_in_taglist(disk, taglist):
35c42d
-            # Taglist is a list containing mix of disk names and tags into which disk may belong.
35c42d
-            # Check if it does. Raise ValueError if unknown tag is encountered.
35c42d
-            if disk.name in taglist:
35c42d
-                return True
35c42d
-            tags = [t[1:] for t in taglist if t.startswith("@")]
35c42d
-            for tag in tags:
35c42d
-                if tag not in Tags.__members__:
35c42d
-                    raise ValueError("unknown ignoredisk tag '@%s' encountered" % tag)
35c42d
-                if Tags(tag) in disk.tags:
35c42d
-                    return True
35c42d
-            return False
35c42d
-
35c42d
-        return ((self.ignored_disks and disk_in_taglist(disk, self.ignored_disks)) or
35c42d
-                (self.exclusive_disks and not disk_in_taglist(disk, self.exclusive_disks)))
35c42d
+        return ((self.ignored_disks and self._disk_in_taglist(disk, self.ignored_disks)) or
35c42d
+                (self.exclusive_disks and not self._disk_in_taglist(disk, self.exclusive_disks)))
35c42d
 
35c42d
     def _hide_ignored_disks(self):
35c42d
         # hide any subtrees that begin with an ignored disk
35c42d
         for disk in [d for d in self._devices if d.is_disk]:
35c42d
-            if self._is_ignored_disk(disk):
35c42d
+            is_ignored = self.ignored_disks and self._disk_in_taglist(disk, self.ignored_disks)
35c42d
+            is_exclusive = self.exclusive_disks and self._disk_in_taglist(disk, self.exclusive_disks)
35c42d
+
35c42d
+            if is_ignored:
35c42d
+                if len(disk.children) == 1:
35c42d
+                    if not all(self._is_ignored_disk(d) for d in disk.children[0].parents):
35c42d
+                        raise DeviceTreeError("Including only a subset of raid/multipath member disks is not allowed.")
35c42d
+
35c42d
+                    # and also children like fwraid or mpath
35c42d
+                    self.hide(disk.children[0])
35c42d
+
35c42d
+                # this disk is ignored: ignore it and all it's potential parents
35c42d
+                for p in disk.parents:
35c42d
+                    self.hide(p)
35c42d
+
35c42d
+                # and finally hide the disk itself
35c42d
+                self.hide(disk)
35c42d
+
35c42d
+            if self.exclusive_disks and not is_exclusive:
35c42d
                 ignored = True
35c42d
                 # If the filter allows all members of a fwraid or mpath, the
35c42d
                 # fwraid or mpath itself is implicitly allowed as well. I don't
35c42d
diff --git a/tests/devicetree_test.py b/tests/devicetree_test.py
35c42d
index a8f369cf..6032e7f6 100644
35c42d
--- a/tests/devicetree_test.py
35c42d
+++ b/tests/devicetree_test.py
35c42d
@@ -370,51 +370,6 @@ class DeviceTreeTestCase(unittest.TestCase):
35c42d
         self.assertTrue(sdb in tree.devices)
35c42d
         self.assertTrue(sdc in tree.devices)
35c42d
 
35c42d
-        # now test exclusive_disks special cases for multipath
35c42d
-        sda.format = get_format("multipath_member", exists=True)
35c42d
-        sdb.format = get_format("multipath_member", exists=True)
35c42d
-        sdc.format = get_format("multipath_member", exists=True)
35c42d
-        mpatha = MultipathDevice("mpatha", parents=[sda, sdb, sdc])
35c42d
-        tree._add_device(mpatha)
35c42d
-
35c42d
-        tree.ignored_disks = []
35c42d
-        tree.exclusive_disks = ["mpatha"]
35c42d
-
35c42d
-        with patch.object(tree, "hide") as hide:
35c42d
-            tree._hide_ignored_disks()
35c42d
-            self.assertFalse(hide.called)
35c42d
-
35c42d
-        tree._hide_ignored_disks()
35c42d
-        self.assertTrue(sda in tree.devices)
35c42d
-        self.assertTrue(sdb in tree.devices)
35c42d
-        self.assertTrue(sdc in tree.devices)
35c42d
-        self.assertTrue(mpatha in tree.devices)
35c42d
-
35c42d
-        # all members in exclusive_disks implies the mpath in exclusive_disks
35c42d
-        tree.exclusive_disks = ["sda", "sdb", "sdc"]
35c42d
-        with patch.object(tree, "hide") as hide:
35c42d
-            tree._hide_ignored_disks()
35c42d
-            self.assertFalse(hide.called)
35c42d
-
35c42d
-        tree._hide_ignored_disks()
35c42d
-        self.assertTrue(sda in tree.devices)
35c42d
-        self.assertTrue(sdb in tree.devices)
35c42d
-        self.assertTrue(sdc in tree.devices)
35c42d
-        self.assertTrue(mpatha in tree.devices)
35c42d
-
35c42d
-        tree.exclusive_disks = ["sda", "sdb"]
35c42d
-        with patch.object(tree, "hide") as hide:
35c42d
-            tree._hide_ignored_disks()
35c42d
-            hide.assert_any_call(mpatha)
35c42d
-            hide.assert_any_call(sdc)
35c42d
-
35c42d
-        # verify that hide works as expected
35c42d
-        tree._hide_ignored_disks()
35c42d
-        self.assertTrue(sda in tree.devices)
35c42d
-        self.assertTrue(sdb in tree.devices)
35c42d
-        self.assertFalse(sdc in tree.devices)
35c42d
-        self.assertFalse(mpatha in tree.devices)
35c42d
-
35c42d
     def test_get_related_disks(self):
35c42d
         tree = DeviceTree()
35c42d
 
35c42d
@@ -447,3 +402,115 @@ class DeviceTreeTestCase(unittest.TestCase):
35c42d
         tree.unhide(sda)
35c42d
         self.assertEqual(tree.get_related_disks(sda), set([sda, sdb]))
35c42d
         self.assertEqual(tree.get_related_disks(sdb), set([sda, sdb]))
35c42d
+
35c42d
+
35c42d
+class DeviceTreeIgnoredExclusiveMultipathTestCase(unittest.TestCase):
35c42d
+
35c42d
+    def setUp(self):
35c42d
+        self.tree = DeviceTree()
35c42d
+
35c42d
+        self.sda = DiskDevice("sda")
35c42d
+        self.sdb = DiskDevice("sdb")
35c42d
+        self.sdc = DiskDevice("sdc")
35c42d
+
35c42d
+        self.tree._add_device(self.sda)
35c42d
+        self.tree._add_device(self.sdb)
35c42d
+        self.tree._add_device(self.sdc)
35c42d
+
35c42d
+        self.assertTrue(self.sda in self.tree.devices)
35c42d
+        self.assertTrue(self.sdb in self.tree.devices)
35c42d
+        self.assertTrue(self.sdc in self.tree.devices)
35c42d
+
35c42d
+        # now test exclusive_disks special cases for multipath
35c42d
+        self.sda.format = get_format("multipath_member", exists=True)
35c42d
+        self.sdb.format = get_format("multipath_member", exists=True)
35c42d
+        self.sdc.format = get_format("multipath_member", exists=True)
35c42d
+        self.mpatha = MultipathDevice("mpatha", parents=[self.sda, self.sdb, self.sdc])
35c42d
+        self.tree._add_device(self.mpatha)
35c42d
+
35c42d
+    def test_exclusive_disks_multipath_1(self):
35c42d
+        # multipath is exclusive -> all disks should be exclusive
35c42d
+        self.tree.ignored_disks = []
35c42d
+        self.tree.exclusive_disks = ["mpatha"]
35c42d
+
35c42d
+        with patch.object(self.tree, "hide") as hide:
35c42d
+            self.tree._hide_ignored_disks()
35c42d
+            self.assertFalse(hide.called)
35c42d
+
35c42d
+        self.tree._hide_ignored_disks()
35c42d
+        self.assertTrue(self.sda in self.tree.devices)
35c42d
+        self.assertTrue(self.sdb in self.tree.devices)
35c42d
+        self.assertTrue(self.sdc in self.tree.devices)
35c42d
+        self.assertTrue(self.mpatha in self.tree.devices)
35c42d
+
35c42d
+    def test_exclusive_disks_multipath_2(self):
35c42d
+        # all disks exclusive -> mpath should also be exclusive
35c42d
+        self.tree.exclusive_disks = ["sda", "sdb", "sdc"]
35c42d
+        with patch.object(self.tree, "hide") as hide:
35c42d
+            self.tree._hide_ignored_disks()
35c42d
+            self.assertFalse(hide.called)
35c42d
+
35c42d
+        self.tree._hide_ignored_disks()
35c42d
+        self.assertTrue(self.sda in self.tree.devices)
35c42d
+        self.assertTrue(self.sdb in self.tree.devices)
35c42d
+        self.assertTrue(self.sdc in self.tree.devices)
35c42d
+        self.assertTrue(self.mpatha in self.tree.devices)
35c42d
+
35c42d
+    def test_exclusive_disks_multipath_3(self):
35c42d
+        # some disks exclusive -> mpath should be hidden
35c42d
+        self.tree.exclusive_disks = ["sda", "sdb"]
35c42d
+        with patch.object(self.tree, "hide") as hide:
35c42d
+            self.tree._hide_ignored_disks()
35c42d
+            hide.assert_any_call(self.mpatha)
35c42d
+            hide.assert_any_call(self.sdc)
35c42d
+
35c42d
+        # verify that hide works as expected
35c42d
+        self.tree._hide_ignored_disks()
35c42d
+        self.assertTrue(self.sda in self.tree.devices)
35c42d
+        self.assertTrue(self.sdb in self.tree.devices)
35c42d
+        self.assertFalse(self.sdc in self.tree.devices)
35c42d
+        self.assertFalse(self.mpatha in self.tree.devices)
35c42d
+
35c42d
+    def test_ignored_disks_multipath_1(self):
35c42d
+        # mpatha ignored -> disks should be hidden
35c42d
+        self.tree.ignored_disks = ["mpatha"]
35c42d
+        self.tree.exclusive_disks = []
35c42d
+
35c42d
+        with patch.object(self.tree, "hide") as hide:
35c42d
+            self.tree._hide_ignored_disks()
35c42d
+            hide.assert_any_call(self.mpatha)
35c42d
+            hide.assert_any_call(self.sda)
35c42d
+            hide.assert_any_call(self.sdb)
35c42d
+            hide.assert_any_call(self.sdc)
35c42d
+
35c42d
+        self.tree._hide_ignored_disks()
35c42d
+        self.assertFalse(self.sda in self.tree.devices)
35c42d
+        self.assertFalse(self.sdb in self.tree.devices)
35c42d
+        self.assertFalse(self.sdc in self.tree.devices)
35c42d
+        self.assertFalse(self.mpatha in self.tree.devices)
35c42d
+
35c42d
+    def test_ignored_disks_multipath_2(self):
35c42d
+        # all disks ignored -> mpath should be hidden
35c42d
+        self.tree.ignored_disks = ["sda", "sdb", "sdc"]
35c42d
+        self.tree.exclusive_disks = []
35c42d
+
35c42d
+        with patch.object(self.tree, "hide") as hide:
35c42d
+            self.tree._hide_ignored_disks()
35c42d
+            hide.assert_any_call(self.mpatha)
35c42d
+            hide.assert_any_call(self.sda)
35c42d
+            hide.assert_any_call(self.sdb)
35c42d
+            hide.assert_any_call(self.sdc)
35c42d
+
35c42d
+        self.tree._hide_ignored_disks()
35c42d
+        self.assertFalse(self.sda in self.tree.devices)
35c42d
+        self.assertFalse(self.sdb in self.tree.devices)
35c42d
+        self.assertFalse(self.sdc in self.tree.devices)
35c42d
+        self.assertFalse(self.mpatha in self.tree.devices)
35c42d
+
35c42d
+    def test_ignored_disks_multipath_3(self):
35c42d
+        # some disks ignored -> error
35c42d
+        self.tree.ignored_disks = ["sda", "sdb"]
35c42d
+        self.tree.exclusive_disks = []
35c42d
+
35c42d
+        with self.assertRaises(DeviceTreeError):
35c42d
+            self.tree._hide_ignored_disks()
35c42d
-- 
35c42d
2.25.4
35c42d