[blivet:master 7/8] Refactor mdraid tests.

mulhern amulhern at redhat.com
Fri Jul 25 16:39:27 UTC 2014


Also, add some mddetail test cases and extend tests for mdremove and mdadd.

Signed-off-by: mulhern <amulhern at redhat.com>
---
 tests/devicelibs_test/mdraid_interrogate_test.py | 220 +++++++++++++++++++++++
 tests/devicelibs_test/mdraid_test.py             | 169 ++++++++---------
 2 files changed, 291 insertions(+), 98 deletions(-)
 create mode 100755 tests/devicelibs_test/mdraid_interrogate_test.py

diff --git a/tests/devicelibs_test/mdraid_interrogate_test.py b/tests/devicelibs_test/mdraid_interrogate_test.py
new file mode 100755
index 0000000..4399da9
--- /dev/null
+++ b/tests/devicelibs_test/mdraid_interrogate_test.py
@@ -0,0 +1,220 @@
+#!/usr/bin/python
+import unittest
+import time
+import uuid
+
+import blivet.devicelibs.raid as raid
+import blivet.devicelibs.mdraid as mdraid
+
+from . import mdraid_test
+
+
+class MDRaidInterrogateTestCase(mdraid_test.MDRaidAsRootTestCase):
+
+    def _matchNames(self, found, expected, transient=None):
+        """ Match names found against expected names.
+
+            :param found: a list of names found in result
+            :type found: list of str
+            :param expected: a list of expected names
+            :type expected: list of str
+            :param transient: a list of names that only sometimes appear
+            :type transient: list of str
+        """
+        transient = transient or []
+
+        for n in (n for n in expected if n not in transient):
+            self.assertIn(n, found, msg="name '%s' not in info" % n)
+
+        for n in (n for n in found if n not in transient):
+            self.assertIn(n, expected, msg="unexpected name '%s' in info" % n)
+
+
+class MDExamineTestCase(MDRaidInterrogateTestCase):
+
+    def __init__(self, methodName='runTest'):
+        super(MDExamineTestCase, self).__init__(methodName=methodName, deviceSpec=[102400, 102400, 102400])
+
+    names_0 = [
+      'DEVICE',
+      'MD_DEVICES',
+      'MD_EVENTS',
+      'MD_LEVEL',
+      'MD_UPDATE_TIME',
+      'MD_UUID'
+    ]
+
+    names_1 = [
+       'DEVICE',
+       'MD_ARRAY_SIZE',
+       'MD_DEV_UUID',
+       'MD_DEVICES',
+       'MD_EVENTS',
+       'MD_LEVEL',
+       'MD_METADATA',
+       'MD_NAME',
+       'MD_UPDATE_TIME',
+       'MD_UUID'
+    ]
+
+    names_container = [
+       'MD_DEVICES',
+       'MD_LEVEL',
+       'MD_METADATA',
+       'MD_UUID'
+    ]
+
+    def testMDExamineMDRaidArray(self):
+        mdraid.mdcreate(self._dev_name, raid.RAID1, self.loopDevices)
+        time.sleep(2) # wait for raid to settle
+
+        # examining the array itself yield no data
+        info = mdraid.mdexamine(self._dev_name)
+        self.assertEqual(info, {})
+
+    def testMDExamineNonMDRaid(self):
+        # invoking mdexamine on a device that is not an array member yields {}
+        info = mdraid.mdexamine(self.loopDevices[0])
+        self.assertEqual(info, {})
+
+    def _testMDExamine(self, names, metadataVersion=None, level=None, spares=0):
+        """ Test mdexamine for a specified metadataVersion.
+
+            :param list names: mdexamine's expected list of names to return
+            :param str metadataVersion: the metadata version for the array
+            :param object level: any valid RAID level descriptor
+            :param int spares: the number of spares in this array
+
+            Verifies that:
+              - exactly the predicted names are returned by mdexamine
+              - RAID level and number of devices are correct
+              - UUIDs have canonical form
+        """
+        level = mdraid.RAID_levels.raidLevel(level or raid.RAID1)
+        mdraid.mdcreate(self._dev_name, level, self.loopDevices, metadataVer=metadataVersion, spares=spares)
+        time.sleep(2) # wait for raid to settle
+
+        info = mdraid.mdexamine(self.loopDevices[0])
+
+        self._matchNames(info.keys(), names)
+
+        # check names with predictable values
+        self.assertEqual(info['MD_DEVICES'], str(len(self.loopDevices) - spares))
+        self.assertEqual(info['MD_LEVEL'], str(level))
+
+        # verify that uuids are in canonical form
+        for name in (k for k in iter(info.keys()) if k.endswith('UUID')):
+            self.assertEqual(str(uuid.UUID(info[name])), info[name])
+
+    def testMDExamineContainerDefault(self):
+        self._testMDExamine(self.names_container, level="container")
+
+    def testMDExamineDefault(self):
+        self._testMDExamine(self.names_1)
+
+    def testMDExamineSpares(self):
+        self._testMDExamine(self.names_1, spares=1)
+
+    def testMDExamine0(self):
+        self._testMDExamine(self.names_0, metadataVersion='0')
+
+    def testMDExamine0_90(self):
+        self._testMDExamine(self.names_0, metadataVersion='0.90')
+
+    def testMDExamine1(self):
+        self._testMDExamine(self.names_1, metadataVersion='1')
+
+    def testMDExamine1_2(self):
+        self._testMDExamine(self.names_1, metadataVersion='1.2')
+
+class MDDetailTestCase(MDRaidInterrogateTestCase):
+
+    def __init__(self, methodName='runTest'):
+        super(MDDetailTestCase, self).__init__(methodName=methodName, deviceSpec=[102400, 102400, 102400])
+
+    names = [
+        'ACTIVE DEVICES',
+        'ARRAY SIZE',
+        'CREATION TIME',
+        'EVENTS',
+        'FAILED DEVICES',
+        'NAME',
+        'PERSISTENCE',
+        'RAID DEVICES',
+        'RAID LEVEL',
+        'RESYNC STATUS',
+        'SPARE DEVICES',
+        'STATE',
+        'TOTAL DEVICES',
+        'UPDATE TIME',
+        'USED DEV SIZE',
+        'UUID',
+        'VERSION',
+        'WORKING DEVICES'
+    ]
+
+    names_0 = [
+        'ACTIVE DEVICES',
+        'ARRAY SIZE',
+        'CREATION TIME',
+        'EVENTS',
+        'FAILED DEVICES',
+        'PERSISTENCE',
+        'PREFERRED MINOR',
+        'RAID DEVICES',
+        'RAID LEVEL',
+        'RESYNC STATUS',
+        'SPARE DEVICES',
+        'STATE',
+        'TOTAL DEVICES',
+        'UPDATE TIME',
+        'USED DEV SIZE',
+        'UUID',
+        'VERSION',
+        'WORKING DEVICES'
+    ]
+
+    def _testMDDetail(self, names, metadataVersion=None, level=None, spares=0):
+        """ Test mddetail for a specified metadataVersion.
+
+            :param list names: mdexamine's expected list of names to return
+            :param str metadataVersion: the metadata version for the array
+            :param object level: any valid RAID level descriptor
+            :param int spares: the number of spares for this array
+
+            Verifies that:
+              - exactly the predicted names are returned by mddetail
+              - UUIDs have canonical form
+        """
+        level = mdraid.RAID_levels.raidLevel(level) if level is not None else raid.RAID1
+
+        mdraid.mdcreate(self._dev_name, level, self.loopDevices, metadataVer=metadataVersion, spares=spares)
+        time.sleep(2) # wait for raid to settle
+
+        info = mdraid.mddetail(self._dev_name)
+
+        # info contains values for exactly names
+        self._matchNames(info.keys(), names, ['RESYNC STATUS'])
+
+        # check names with predictable values
+        self.assertEqual(info['ACTIVE DEVICES'], str(len(self.loopDevices) - spares))
+        self.assertEqual(info['FAILED DEVICES'], '0')
+        self.assertEqual(info['RAID LEVEL'], str(level))
+        self.assertEqual(info['SPARE DEVICES'], str(spares))
+        self.assertEqual(info['TOTAL DEVICES'], str(len(self.loopDevices)))
+        self.assertEqual(info['WORKING DEVICES'], str(len(self.loopDevices)))
+
+        # verify that uuid is in canonical form
+        self.assertEqual(str(uuid.UUID(info['UUID'])), info['UUID'])
+
+    def testMDDetail(self):
+        self._testMDDetail(self.names)
+
+    def testMDDetailSpares(self):
+        self._testMDDetail(self.names, spares=1)
+
+    def testMDDetail0_90(self):
+        self._testMDDetail(self.names_0, metadataVersion='0.90')
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/devicelibs_test/mdraid_test.py b/tests/devicelibs_test/mdraid_test.py
index 107423e..2ed7a2a 100755
--- a/tests/devicelibs_test/mdraid_test.py
+++ b/tests/devicelibs_test/mdraid_test.py
@@ -1,7 +1,6 @@
 #!/usr/bin/python
 import unittest
 import time
-import uuid
 
 import blivet.devicelibs.raid as raid
 import blivet.devicelibs.mdraid as mdraid
@@ -49,149 +48,123 @@ class MDRaidTestCase(unittest.TestCase):
                                                          version="version"),
                          mdraid.MD_SUPERBLOCK_SIZE)
 
-
 class MDRaidAsRootTestCase(loopbackedtestcase.LoopBackedTestCase):
 
-    names_0 = [
-      'DEVICE',
-      'MD_DEVICES',
-      'MD_EVENTS',
-      'MD_LEVEL',
-      'MD_UPDATE_TIME',
-      'MD_UUID'
-    ]
-
-    names_1 = [
-       'DEVICE',
-       'MD_ARRAY_SIZE',
-       'MD_DEV_UUID',
-       'MD_DEVICES',
-       'MD_EVENTS',
-       'MD_LEVEL',
-       'MD_METADATA',
-       'MD_NAME',
-       'MD_UPDATE_TIME',
-       'MD_UUID'
-    ]
-
-    names_container = [
-       'MD_DEVICES',
-       'MD_LEVEL',
-       'MD_METADATA',
-       'MD_UUID'
-    ]
-
-    def __init__(self, methodName='runTest'):
-        """Set up the structure of the mdraid array."""
-        super(MDRaidAsRootTestCase, self).__init__(methodName=methodName)
+    def __init__(self, methodName='runTest', deviceSpec=None):
+        super(MDRaidAsRootTestCase, self).__init__(methodName=methodName, deviceSpec=deviceSpec)
         self._dev_name = "/dev/md0"
 
     def tearDown(self):
         try:
             mdraid.mddeactivate(self._dev_name)
             for dev in self.loopDevices:
+                mdraid.mdremove(self._dev_name, dev, fail=True)
                 mdraid.mddestroy(dev)
         except MDRaidError:
             pass
 
         super(MDRaidAsRootTestCase, self).tearDown()
 
-    def testMDExamineMDRaidArray(self):
-        mdraid.mdcreate(self._dev_name, raid.RAID1, self.loopDevices)
-        # wait for raid to settle
-        time.sleep(2)
-
-        # examining the array itself yield no data
-        info = mdraid.mdexamine(self._dev_name)
-        self.assertEqual(info, {})
+class RAID0Test(MDRaidAsRootTestCase):
 
-    def testMDExamineNonMDRaid(self):
-        # invoking mdexamine on a device that is not an array member yields {}
-        info = mdraid.mdexamine(self.loopDevices[0])
-        self.assertEqual(info, {})
-
-    def _testMDExamine(self, names, metadataVersion=None, level=None):
-        """ Test mdexamine for a specified metadataVersion.
-
-            :param list names: mdexamine's expected list of names to return
-            :param str metadataVersion: the metadata version for the array
-            :param object level: any valid RAID level descriptor
+    def __init__(self, methodName='runTest'):
+        super(RAID0Test, self).__init__(methodName=methodName, deviceSpec=[102400, 102400, 102400])
 
-            Verifies that:
-              - exactly the predicted names are returned by mdexamine
-              - RAID level and number of devices are correct
-              - UUIDs have canonical form
-        """
-        level = mdraid.RAID_levels.raidLevel(level or raid.RAID1)
-        mdraid.mdcreate(self._dev_name, level, self.loopDevices, metadataVer=metadataVersion)
-        # wait for raid to settle
-        time.sleep(2)
+    def testGrow(self):
+        self.assertIsNone(mdraid.mdcreate(self._dev_name, raid.RAID0, self.loopDevices[:2]))
+        time.sleep(2) # wait for raid to settle
 
-        info = mdraid.mdexamine(self.loopDevices[0])
+        # it is not possible to add a managed device to a non-redundant array,
+        # but it can be grown, by specifying the desired number of members
+        self.assertIsNone(mdraid.mdadd(self._dev_name, self.loopDevices[2], raid_devices=3))
 
-        # info contains values for exactly names
-        for n in names:
-            self.assertIn(n, info, msg="name '%s' not in info" % n)
+    def testGrowRAID1(self):
+        self.assertIsNone(mdraid.mdcreate(self._dev_name, raid.RAID1, self.loopDevices[:2]))
+        time.sleep(2) # wait for raid to settle
 
-        for n in info.keys():
-            self.assertIn(n, names, msg="unexpected name '%s' in info" % n)
+        # a RAID1 array can be grown as well as a RAID0 array
+        self.assertIsNone(mdraid.mdadd(self._dev_name, self.loopDevices[2], raid_devices=3))
 
-        # check names with predictable values
-        self.assertEqual(info['MD_DEVICES'], '2')
-        self.assertEqual(info['MD_LEVEL'], str(level))
+    def testGrowTooBig(self):
+        self.assertIsNone(mdraid.mdcreate(self._dev_name, raid.RAID0, self.loopDevices[:2]))
+        time.sleep(2) # wait for raid to settle
 
-        # verify that uuids are in canonical form
-        for name in (k for k in iter(info.keys()) if k.endswith('UUID')):
-            self.assertTrue(str(uuid.UUID(info[name])) == info[name])
+        # if more devices are specified than are available after the
+        # addition an error is raised
+        with self.assertRaises(MDRaidError):
+            mdraid.mdadd(self._dev_name, self.loopDevices[2], raid_devices=4)
 
-    def testMDExamineContainerDefault(self):
-        self._testMDExamine(self.names_container, level="container")
+    def testGrowSmaller(self):
+        self.assertIsNone(mdraid.mdcreate(self._dev_name, raid.RAID0, self.loopDevices[:2]))
+        time.sleep(2) # wait for raid to settle
 
-    def testMDExamineDefault(self):
-        self._testMDExamine(self.names_1)
+        # it is ok to grow an array smaller than its devices
+        self.assertIsNone(mdraid.mdadd(self._dev_name, self.loopDevices[2], raid_devices=2))
 
-    def testMDExamine0(self):
-        self._testMDExamine(self.names_0, metadataVersion='0')
+    def testGrowSimple(self):
+        self.assertIsNone(mdraid.mdcreate(self._dev_name, raid.RAID0, self.loopDevices[:2]))
+        time.sleep(2) # wait for raid to settle
 
-    def testMDExamine0_90(self):
-        self._testMDExamine(self.names_0, metadataVersion='0.90')
+        # try to simply add a device and things go wrong
+        with self.assertRaises(MDRaidError):
+            mdraid.mdadd(self._dev_name, self.loopDevices[2])
 
-    def testMDExamine1(self):
-        self._testMDExamine(self.names_1, metadataVersion='1')
+class SimpleRaidTest(MDRaidAsRootTestCase):
 
-    def testMDExamine1_2(self):
-        self._testMDExamine(self.names_1, metadataVersion='1.2')
+    def __init__(self, methodName='runTest'):
+        super(SimpleRaidTest, self).__init__(methodName=methodName, deviceSpec=[102400, 102400, 102400])
 
     def testMDRaidAsRoot(self):
         ##
         ## mdcreate
         ##
         # pass
-        self.assertEqual(mdraid.mdcreate(self._dev_name, raid.RAID1, self.loopDevices), None)
+        self.assertIsNone(mdraid.mdcreate(self._dev_name, raid.RAID1, self.loopDevices))
+        time.sleep(2) # wait for raid to settle
 
         # fail
         with self.assertRaises(MDRaidError):
             mdraid.mdcreate("/dev/md1", "raid1", ["/not/existing/dev0", "/not/existing/dev1"])
 
-        ##
-        ## mddeactivate
-        ##
-        # pass
-        self.assertEqual(mdraid.mddeactivate(self._dev_name), None)
-
         # fail
         with self.assertRaises(MDRaidError):
-            mdraid.mddeactivate("/not/existing/md")
+            mdraid.mdadd(self._dev_name, "/not/existing/device")
+        time.sleep(2) # wait for raid to settle
+
+        # removing and re-adding a component device should succeed
+        self.assertIsNone(mdraid.mdremove(self._dev_name, self.loopDevices[2], fail=True))
+        time.sleep(2) # wait for raid to settle
+        self.assertIsNone(mdraid.mdadd(self._dev_name, self.loopDevices[2]))
+        time.sleep(3) # wait for raid to settle
+
+        # it is not possible to add a device that has already been added
+        with self.assertRaises(MDRaidError):
+            mdraid.mdadd(self._dev_name, self.loopDevices[2])
+
+        self.assertIsNone(mdraid.mdremove(self._dev_name, self.loopDevices[2], fail=True))
+        time.sleep(2) # wait for raid to settle
+
+        # can not re-add incrementally, because the array is active
+        with self.assertRaises(MDRaidError):
+            mdraid.mdadd(None, self.loopDevices[2], incremental=True)
 
         ##
-        ## mdadd
+        ## mddeactivate
         ##
         # pass
-        # TODO
+        self.assertIsNone(mdraid.mddeactivate(self._dev_name))
+
+        # once the array is deactivated, can add incrementally
+        self.assertIsNone(mdraid.mdadd(None, self.loopDevices[2], incremental=True))
+
+        # but cannot re-add twice
+        with self.assertRaises(MDRaidError):
+            mdraid.mdadd(None, self.loopDevices[2], incremental=True)
 
         # fail
         with self.assertRaises(MDRaidError):
-            mdraid.mdadd(self._dev_name, "/not/existing/device")
+            mdraid.mddeactivate("/not/existing/md")
+
 
         ##
         ## mdactivate
@@ -207,7 +180,7 @@ class MDRaidAsRootTestCase(loopbackedtestcase.LoopBackedTestCase):
         ##
         # pass
         for dev in self.loopDevices:
-            self.assertEqual(mdraid.mddestroy(dev), None)
+            self.assertIsNone(mdraid.mddestroy(dev))
 
         # pass
         # Note that these should fail because mdadm is unable to locate the
-- 
1.9.3



More information about the anaconda-patches mailing list