[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[PATCH 11/15] Add device action test suite.



---
 tests/storage_test/Makefile.am    |    2 +-
 tests/storage_test/action_test.py | 1191 +++++++++++++++++++++++++++++++++++++
 2 files changed, 1192 insertions(+), 1 deletions(-)
 create mode 100755 tests/storage_test/action_test.py

diff --git a/tests/storage_test/Makefile.am b/tests/storage_test/Makefile.am
index e734b42..343cfff 100644
--- a/tests/storage_test/Makefile.am
+++ b/tests/storage_test/Makefile.am
@@ -26,4 +26,4 @@ MAINTAINERCLEANFILES = Makefile.in
 ANACDIR = $(top_builddir)/pyanaconda
 TESTS_ENVIRONMENT = PATH=/sbin:/usr/sbin:$$PATH PYTHONPATH=$(top_builddir)/tests:$(ANACDIR)/isys/.libs:$(ANACDIR):$(top_builddir)
 
-TESTS = size_test.py
+TESTS = action_test.py size_test.py
diff --git a/tests/storage_test/action_test.py b/tests/storage_test/action_test.py
new file mode 100755
index 0000000..2fcfc6b
--- /dev/null
+++ b/tests/storage_test/action_test.py
@@ -0,0 +1,1191 @@
+#!/usr/bin/python
+
+import unittest
+from mock import Mock
+from mock import TestCase
+
+import parted
+
+import pyanaconda.anaconda_log
+pyanaconda.anaconda_log.init()
+
+import pyanaconda.iutil
+import pyanaconda.storage as storage
+from pyanaconda.storage.formats import getFormat
+
+# device classes for brevity's sake -- later on, that is
+from pyanaconda.storage.devices import StorageDevice
+from pyanaconda.storage.devices import DiskDevice
+from pyanaconda.storage.devices import PartitionDevice
+from pyanaconda.storage.devices import MDRaidArrayDevice
+from pyanaconda.storage.devices import DMDevice
+from pyanaconda.storage.devices import LUKSDevice
+from pyanaconda.storage.devices import LVMVolumeGroupDevice
+from pyanaconda.storage.devices import LVMLogicalVolumeDevice
+from pyanaconda.storage.devices import FileDevice
+
+# action classes
+from pyanaconda.storage.deviceaction import ActionCreateDevice
+from pyanaconda.storage.deviceaction import ActionResizeDevice
+from pyanaconda.storage.deviceaction import ActionDestroyDevice
+from pyanaconda.storage.deviceaction import ActionCreateFormat
+from pyanaconda.storage.deviceaction import ActionResizeFormat
+from pyanaconda.storage.deviceaction import ActionMigrateFormat
+from pyanaconda.storage.deviceaction import ActionDestroyFormat
+
+""" DeviceActionTestSuite """
+
+class StorageTestCase(TestCase):
+    """ StorageTestCase
+
+        This is a base class for storage test cases. It sets up imports of
+        the storage package, along with an Anaconda instance and a Storage
+        instance. There are lots of little patches to prevent various pieces
+        of code from trying to access filesystems and/or devices on the host
+        system, along with a couple of convenience methods.
+
+    """
+    def __init__(self, *args, **kwargs):
+        TestCase.__init__(self, *args, **kwargs)
+
+        self.setUpAnaconda()
+
+    def setUpAnaconda(self):
+        pyanaconda.iutil.execWithRedirect = Mock()
+        pyanaconda.iutil.execWithCapture = Mock()
+        pyanaconda.iutil.execWithPulseProgress = Mock()
+        pyanaconda.baseudev = Mock()
+
+        self.anaconda = Mock()
+        self.setUpStorage()
+
+    def setUpStorage(self):
+        self.setUpDeviceLibs()
+        self.storage = storage.Storage(self.anaconda)
+
+        # device status
+        StorageDevice.status = False
+        DMDevice.status = False
+        LUKSDevice.status = False
+        LVMVolumeGroupDevice.status = False
+        MDRaidArrayDevice.status = False
+        FileDevice.status = False
+
+        # prevent PartitionDevice from trying to dig around in the partition's
+        # geometry
+        PartitionDevice._setTargetSize = StorageDevice._setTargetSize
+
+        # prevent Ext2FS from trying to run resize2fs to get a filesystem's
+        # minimum size
+        storage.formats.fs.Ext2FS.minSize = storage.formats.DeviceFormat.minSize
+        storage.formats.fs.FS.migratable = storage.formats.DeviceFormat.migratable
+
+    def setUpDeviceLibs(self):
+        # devicelibs shouldn't be touching or looking at the host system
+
+        # lvm is easy because all calls to /sbin/lvm are via lvm()
+        storage.devicelibs.lvm.lvm = Mock()
+
+        # mdraid is easy because all calls to /sbin/mdadm are via mdadm()
+        storage.devicelibs.mdraid.mdadm = Mock()
+
+        # swap
+        storage.devicelibs.swap.swapstatus = Mock(return_value=False)
+        storage.devicelibs.swap.swapon = Mock()
+        storage.devicelibs.swap.swapoff = Mock()
+
+        # dm
+        storage.devicelibs.dm = Mock()
+
+        # crypto/luks
+	storage.devicelibs.crypto.luks_status = Mock(return_value=False)
+	storage.devicelibs.crypto.luks_uuid = Mock()
+	storage.devicelibs.crypto.luks_format = Mock()
+	storage.devicelibs.crypto.luks_open = Mock()
+	storage.devicelibs.crypto.luks_close = Mock()
+	storage.devicelibs.crypto.luks_add_key = Mock()
+	storage.devicelibs.crypto.luks_remove_key = Mock()
+
+        # this list would normally be obtained by parsing /proc/mdstat
+	storage.devicelibs.mdraid.raid_levels = \
+					[storage.devicelibs.mdraid.RAID10,
+					 storage.devicelibs.mdraid.RAID0,
+					 storage.devicelibs.mdraid.RAID1,
+					 storage.devicelibs.mdraid.RAID4,
+					 storage.devicelibs.mdraid.RAID5,
+					 storage.devicelibs.mdraid.RAID6]
+
+    def newDevice(*args, **kwargs):
+        """ Return a new Device instance suitable for testing. """
+        args = args[1:] # drop self arg
+        device_class = kwargs.pop("device_class")
+        exists = kwargs.pop("exists", False)
+        part_type = kwargs.pop("part_type", parted.PARTITION_NORMAL)
+        device = device_class(*args, **kwargs)
+
+        if exists:
+            # set up mock parted.Device w/ correct size
+            device._partedDevice = Mock()
+            device._partedDevice.getSize = Mock(return_value=float(device.size))
+            device._partedDevice.sectorSize = 512
+
+        if isinstance(device, PartitionDevice):
+            #if exists:
+            #    device.parents = device.req_disks
+            device.parents = device.req_disks
+
+            partedPartition = Mock()
+
+            if device.disk:
+                part_num = device.name[len(device.disk.name):].split("p")[-1]
+                partedPartition.number = int(part_num)
+
+            partedPartition.type = part_type
+            partedPartition.path = device.path
+            partedPartition.getDeviceNodeName = Mock(return_value=device.name)
+            partedPartition.getSize = Mock(return_value=float(device.size))
+            device._partedPartition = partedPartition
+
+        device.exists = exists
+        device.format.exists = exists
+
+        if isinstance(device, PartitionDevice):
+            # PartitionDevice.probe sets up data needed for resize operations
+            device.probe()
+
+        return device
+
+    def newFormat(*args, **kwargs):
+        """ Return a new DeviceFormat instance suitable for testing.
+
+            Keyword Arguments:
+
+                device_instance - StorageDevice instance this format will be
+                                  created on. This is needed for setup of
+                                  resizable formats.
+
+            All other arguments are passed directly to
+            pyanaconda.storage.formats.getFormat.
+        """
+        args = args[1:] # drop self arg
+        exists = kwargs.pop("exists", False)
+        device_instance = kwargs.pop("device_instance", None)
+        format = getFormat(*args, **kwargs)
+        if isinstance(format, storage.formats.disklabel.DiskLabel):
+            format._partedDevice = Mock()
+            format._partedDisk = Mock()
+
+        format.exists = exists
+
+        if format.resizable and device_instance:
+            format._size = device_instance.currentSize
+
+        return format
+
+    def destroyAllDevices(self, disks=None):
+        """ Remove all devices from the devicetree.
+
+            Keyword Arguments:
+
+                disks - a list of names of disks to remove partitions from
+
+            Note: this is largely ripped off from partitioning.clearPartitions.
+
+        """
+        partitions = self.storage.partitions
+
+        # Sort partitions by descending partition number to minimize confusing
+        # things like multiple "destroy sda5" actions due to parted renumbering
+        # partitions. This can still happen through the UI but it makes sense to
+        # avoid it where possible.
+        partitions.sort(key=lambda p: p.partedPartition.number, reverse=True)
+        for part in partitions:
+            if disks and part.disk.name not in disks:
+                continue
+
+            devices = self.storage.deviceDeps(part)
+            while devices:
+                leaves = [d for d in devices if d.isleaf]
+                for leaf in leaves:
+                    self.storage.destroyDevice(leaf)
+                    devices.remove(leaf)
+
+            self.storage.destroyDevice(part)
+
+    def scheduleCreateDevice(self, *args, **kwargs):
+        """ Schedule an action to create the specified device.
+
+            Verify that the device is not already in the tree and that the
+            act of scheduling/registering the action also adds the device to
+            the tree.
+
+            Return the DeviceAction instance.
+        """
+        device = kwargs.pop("device")
+	if hasattr(device, "req_disks") and \
+	   len(device.req_disks) == 1 and \
+	   not device.parents:
+	    device.parents = device.req_disks
+
+        devicetree = self.storage.devicetree
+
+        self.assertEqual(devicetree.getDeviceByName(device.name), None)
+        action = storage.deviceaction.ActionCreateDevice(device)
+        devicetree.registerAction(action)
+        self.assertEqual(devicetree.getDeviceByName(device.name), device)
+        return action
+
+    def scheduleDestroyDevice(self, *args, **kwargs):
+        """ Schedule an action to destroy the specified device.
+
+            Verify that the device exists initially and that the act of
+            scheduling/registering the action also removes the device from
+            the tree.
+
+            Return the DeviceAction instance.
+        """
+        device = kwargs.pop("device")
+        devicetree = self.storage.devicetree
+
+        self.assertEqual(devicetree.getDeviceByName(device.name), device)
+        action = storage.deviceaction.ActionDestroyDevice(device)
+        devicetree.registerAction(action)
+        self.assertEqual(devicetree.getDeviceByName(device.name), None)
+        return action
+
+    def scheduleCreateFormat(self, *args, **kwargs):
+        """ Schedule an action to write a new format to a device.
+
+            Verify that the device is already in the tree, that it is not
+            already set up to contain the specified format, and that the act
+            of registering/scheduling the action causes the new format to be
+            reflected in the tree.
+
+            Return the DeviceAction instance.
+        """
+        device = kwargs.pop("device")
+        format = kwargs.pop("format")
+        devicetree = self.storage.devicetree
+
+        self.assertNotEqual(device.format, format)
+        self.assertEqual(devicetree.getDeviceByName(device.name), device)
+        action = storage.deviceaction.ActionCreateFormat(device, format)
+        devicetree.registerAction(action)
+        _device = devicetree.getDeviceByName(device.name)
+        self.assertEqual(_device.format, format)
+        return action
+
+    def scheduleDestroyFormat(self, *args, **kwargs):
+        """ Schedule an action to remove a format from a device.
+
+            Verify that the device is already in the tree and that the act
+            of registering/scheduling the action causes the new format to be
+            reflected in the tree.
+
+            Return the DeviceAction instance.
+        """
+        device = kwargs.pop("device")
+        devicetree = self.storage.devicetree
+
+        self.assertEqual(devicetree.getDeviceByName(device.name), device)
+        action = storage.deviceaction.ActionDestroyFormat(device)
+        devicetree.registerAction(action)
+        _device = devicetree.getDeviceByName(device.name)
+        self.assertEqual(_device.format.type, None)
+        return action
+
+
+class DeviceActionTestCase(StorageTestCase):
+    def setUp(self):
+        """ Create something like a preexisting autopart on two disks (sda,sdb).
+
+            The other two disks (sdc,sdd) are left for individual tests to use.
+        """
+        self.setUpAnaconda()
+
+        for name in ["sda", "sdb", "sdc", "sdd"]:
+            disk = self.newDevice(device_class=DiskDevice,
+                                  name=name, size=100000)
+            disk.format = self.newFormat("disklabel", path=disk.path,
+                                         exists=True)
+            self.storage.devicetree._addDevice(disk)
+
+        # create a layout similar to autopart as a starting point
+        sda = self.storage.devicetree.getDeviceByName("sda")
+        sdb = self.storage.devicetree.getDeviceByName("sdb")
+
+        sda1 = self.newDevice(device_class=PartitionDevice,
+                              exists=True, name="sda1", parents=[sda], size=500)
+        sda1.format = self.newFormat("ext4", mountpoint="/boot",
+                                     device_instance=sda1,
+                                     device=sda1.path, exists=True)
+        self.storage.devicetree._addDevice(sda1)
+
+        sda2 = self.newDevice(device_class=PartitionDevice,
+                              size=99500, name="sda2", parents=[sda], exists=True)
+        sda2.format = self.newFormat("lvmpv", device=sda2.path, exists=True)
+        self.storage.devicetree._addDevice(sda2)
+
+        sdb1 = self.newDevice(device_class=PartitionDevice,
+                              size=99999, name="sdb1", parents=[sdb], exists=True)
+        sdb1.format = self.newFormat("lvmpv", device=sdb1.path, exists=True)
+        self.storage.devicetree._addDevice(sdb1)
+
+        vg = self.newDevice(device_class=LVMVolumeGroupDevice,
+                            name="VolGroup", parents=[sda2, sdb1],
+                            exists=True)
+        self.storage.devicetree._addDevice(vg)
+
+        lv_root = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                 name="lv_root", vgdev=vg, size=160000,
+                                 exists=True)
+        lv_root.format = self.newFormat("ext4", mountpoint="/",
+                                        device_instance=lv_root,
+                                        device=lv_root.path, exists=True)
+        self.storage.devicetree._addDevice(lv_root)
+
+        lv_swap = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                 name="lv_swap", vgdev=vg, size=4000,
+                                 exists=True)
+        lv_swap.format = self.newFormat("swap", device=lv_swap.path,
+                                        device_instance=lv_swap,
+                                        exists=True)
+        self.storage.devicetree._addDevice(lv_swap)
+
+    def testActions(self, *args, **kwargs):
+        """ Verify correct management of actions.
+
+            - action creation/registration/cancellation
+                - ActionCreateDevice adds device to tree
+                - ActionDestroyDevice removes device from tree
+                - ActionCreateFormat sets device.format in tree
+                - ActionDestroyFormat unsets device.format in tree
+                - cancelled action's registration side-effects reversed
+                - failure to register destruction of non-leaf device
+                - failure to register creation of device already in tree?
+                - failure to register destruction of device not in tree?
+
+            - action pruning
+                - non-existent-device create..destroy cycles removed
+                    - all actions on this device should get removed
+                - all actions pruned from to-be-destroyed devices
+                    - resize, format, migrate, &c
+                - redundant resize/migrate/format actions pruned
+                    - last one registered stays
+
+            - action sorting
+                - destroy..resize..migrate..create
+                - creation
+                    - leaves-last, including formatting
+                - destruction
+                    - leaves-first
+        """
+        devicetree = self.storage.devicetree
+
+        # clear the disks
+        self.destroyAllDevices()
+        self.assertEqual(devicetree.getDevicesByType("lvmlv"), [])
+        self.assertEqual(devicetree.getDevicesByType("lvmvg"), [])
+        self.assertEqual(devicetree.getDevicesByType("partition"), [])
+
+        sda = devicetree.getDeviceByName("sda")
+        self.assertNotEqual(sda, None, "failed to find disk 'sda'")
+
+        sda1 = self.newDevice(device_class=PartitionDevice,
+                              name="sda1", size=500, parents=[sda])
+        self.scheduleCreateDevice(device=sda1)
+
+        sda2 = self.newDevice(device_class=PartitionDevice,
+                              name="sda2", size=100000, parents=[sda])
+        self.scheduleCreateDevice(device=sda2)
+        format = self.newFormat("lvmpv", device=sda2.path)
+        self.scheduleCreateFormat(device=sda2, format=format)
+
+        vg = self.newDevice(device_class=LVMVolumeGroupDevice,
+                            name="vg", parents=[sda2])
+        self.scheduleCreateDevice(device=vg)
+
+        lv_root = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                 name="lv_root", vgdev=vg, size=60000)
+        self.scheduleCreateDevice(device=lv_root)
+        format = self.newFormat("ext4", device=lv_root.path, mountpoint="/")
+        self.scheduleCreateFormat(device=lv_root, format=format)
+
+        lv_swap = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                 name="lv_swap", vgdev=vg, size=4000)
+        self.scheduleCreateDevice(device=lv_swap)
+        format = self.newFormat("swap", device=lv_swap.path)
+        self.scheduleCreateFormat(device=lv_swap, format=format)
+
+        sda3 = self.newDevice(device_class=PartitionDevice,
+                              name="sda3", parents=[sda], size=40000)
+        self.scheduleCreateDevice(device=sda3)
+        format = self.newFormat("mdmember", device=sda3.path)
+        self.scheduleCreateFormat(device=sda3, format=format)
+
+        sdb = devicetree.getDeviceByName("sdb")
+        self.assertNotEqual(sdb, None, "failed to find disk 'sdb'")
+
+        sdb1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdb1", parents=[sdb], size=40000)
+        self.scheduleCreateDevice(device=sdb1)
+        format = self.newFormat("mdmember", device=sdb1.path,)
+        self.scheduleCreateFormat(device=sdb1, format=format)
+
+        md0 = self.newDevice(device_class=MDRaidArrayDevice,
+                             name="md0", level="raid0", minor=0, size=80000,
+                             memberDevices=2, totalDevices=2,
+                             parents=[sdb1, sda3])
+        self.scheduleCreateDevice(device=md0)
+
+        format = self.newFormat("ext4", device=md0.path, mountpoint="/home")
+        self.scheduleCreateFormat(device=md0, format=format)
+
+        format = self.newFormat("ext4", mountpoint="/boot", device=sda1.path)
+        self.scheduleCreateFormat(device=sda1, format=format)
+
+    def testActionCreation(self, *args, **kwargs):
+        """ Verify correct operation of action class constructors. """
+        # instantiation of device resize action for non-existent device should
+        # fail
+        # XXX resizable depends on existence, so this is covered implicitly
+        sdd = self.storage.devicetree.getDeviceByName("sdd")
+        p = self.newDevice(device_class=PartitionDevice,
+                           name="sdd1", size=32768, parents=[sdd])
+        self.failUnlessRaises(ValueError,
+                              storage.deviceaction.ActionResizeDevice,
+                              p,
+                              p.size + 7232)
+
+        # instantiation of device resize action for non-resizable device
+        # should fail
+        vg = self.storage.devicetree.getDeviceByName("VolGroup")
+        self.assertNotEqual(vg, None)
+        self.failUnlessRaises(ValueError,
+                              storage.deviceaction.ActionResizeDevice,
+                              vg,
+                              vg.size + 32)
+
+        # instantiation of format resize action for non-resizable format type
+        # should fail
+        lv_swap = self.storage.devicetree.getDeviceByName("VolGroup-lv_swap")
+        self.assertNotEqual(lv_swap, None)
+        self.failUnlessRaises(ValueError,
+                              storage.deviceaction.ActionResizeFormat,
+                              lv_swap,
+                              lv_swap.size + 32)
+
+        # instantiation of format resize action for non-existent format
+        # should fail
+        lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root")
+        self.assertNotEqual(lv_root, None)
+        lv_root.format.exists = False
+        self.failUnlessRaises(ValueError,
+                              storage.deviceaction.ActionResizeFormat,
+                              lv_root,
+                              lv_root.size - 1000)
+        lv_root.format.exists = True
+
+        # instantiation of format migrate action for non-migratable format
+        # type should fail
+        lv_swap = self.storage.devicetree.getDeviceByName("VolGroup-lv_swap")
+        self.assertNotEqual(lv_swap, None)
+        self.assertEqual(lv_swap.exists, True)
+        self.failUnlessRaises(ValueError,
+                              storage.deviceaction.ActionMigrateFormat,
+                              lv_swap)
+
+        # instantiation of format migrate for non-existent format should fail
+        lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root")
+        self.assertNotEqual(lv_root, None)
+        orig_format = lv_root.format
+        lv_root.format = getFormat("ext3", device=lv_root.path)
+        self.failUnlessRaises(ValueError,
+                              storage.deviceaction.ActionMigrateFormat,
+                              lv_root)
+        lv_root.format = orig_format
+
+        # instantiation of device create action for existing device should
+        # fail
+        lv_swap = self.storage.devicetree.getDeviceByName("VolGroup-lv_swap")
+        self.assertNotEqual(lv_swap, None)
+        self.assertEqual(lv_swap.exists, True)
+        self.failUnlessRaises(ValueError,
+                              storage.deviceaction.ActionCreateDevice,
+                              lv_swap)
+
+        # instantiation of format destroy action for device causes device's
+        # format attribute to be a DeviceFormat instance
+        lv_swap = self.storage.devicetree.getDeviceByName("VolGroup-lv_swap")
+        self.assertNotEqual(lv_swap, None)
+        orig_format = lv_swap.format
+        self.assertEqual(lv_swap.format.type, "swap")
+        a = storage.deviceaction.ActionDestroyFormat(lv_swap)
+        self.assertEqual(lv_swap.format.type, None)
+
+        # instantiation of format create action for device causes new format
+        # to be accessible via device's format attribute
+        new_format = getFormat("vfat", device=lv_swap.path)
+        a = storage.deviceaction.ActionCreateFormat(lv_swap, new_format)
+        self.assertEqual(lv_swap.format, new_format)
+        lv_swap.format = orig_format
+
+    def testActionRegistration(self, *args, **kwargs):
+        """ Verify correct operation of action registration and cancelling. """
+        # self.setUp has just been run, so we should have something like
+        # a preexisting autopart config in the devicetree.
+
+        # registering a destroy action for a non-leaf device should fail
+        vg = self.storage.devicetree.getDeviceByName("VolGroup")
+        self.assertNotEqual(vg, None)
+        self.assertEqual(vg.isleaf, False)
+        a = storage.deviceaction.ActionDestroyDevice(vg)
+        self.failUnlessRaises(ValueError,
+                              self.storage.devicetree.registerAction,
+			      a)
+
+        # registering any action other than create for a device that's not in
+        # the devicetree should fail
+        sdc = self.storage.devicetree.getDeviceByName("sdc")
+        self.assertNotEqual(sdc, None)
+        sdc1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdc1", size=100000, parents=[sdc],
+                              exists=True)
+
+        sdc1_format = self.newFormat("ext2", device=sdc1.path, mountpoint="/")
+        create_sdc1_format = ActionCreateFormat(sdc1, sdc1_format)
+        self.failUnlessRaises(storage.errors.DeviceTreeError,
+                              self.storage.devicetree.registerAction,
+                              create_sdc1_format)
+
+        sdc1_format.exists = True
+
+        migrate_sdc1 = ActionMigrateFormat(sdc1)
+        self.failUnlessRaises(storage.errors.DeviceTreeError,
+                              self.storage.devicetree.registerAction,
+                              migrate_sdc1)
+        migrate_sdc1.cancel()
+
+        resize_sdc1_format = ActionResizeFormat(sdc1, sdc1.size - 10000)
+        self.failUnlessRaises(storage.errors.DeviceTreeError,
+                              self.storage.devicetree.registerAction,
+                              resize_sdc1_format)
+
+        resize_sdc1 = ActionResizeDevice(sdc1, sdc1.size - 10000)
+        self.failUnlessRaises(storage.errors.DeviceTreeError,
+                              self.storage.devicetree.registerAction,
+                              resize_sdc1)
+
+        resize_sdc1.cancel()
+        resize_sdc1_format.cancel()
+
+        destroy_sdc1_format = ActionDestroyFormat(sdc1)
+        self.failUnlessRaises(storage.errors.DeviceTreeError,
+                              self.storage.devicetree.registerAction,
+                              destroy_sdc1_format)
+
+
+        destroy_sdc1 = ActionDestroyDevice(sdc1)
+        self.failUnlessRaises(storage.errors.DeviceTreeError,
+                              self.storage.devicetree.registerAction,
+                              resize_sdc1)
+
+        # registering a device destroy action should cause the device to be
+        # removed from the devicetree
+        lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root")
+        self.assertNotEqual(lv_root, None)
+        a = ActionDestroyDevice(lv_root)
+        self.storage.devicetree.registerAction(a)
+        lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root")
+        self.assertEqual(lv_root, None)
+        self.storage.devicetree.cancelAction(a)
+
+        # registering a device create action should cause the device to be
+        # added to the devicetree
+        sdd = self.storage.devicetree.getDeviceByName("sdd")
+        self.assertNotEqual(sdd, None)
+        sdd1 = self.storage.devicetree.getDeviceByName("sdd1")
+        self.assertEqual(sdd1, None)
+        sdd1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdd1", size=100000, parents=[sdd])
+        a = ActionCreateDevice(sdd1)
+        self.storage.devicetree.registerAction(a)
+        sdd1 = self.storage.devicetree.getDeviceByName("sdd1")
+        self.assertNotEqual(sdd1, None)
+
+    def testActionObsoletes(self, *args, **kwargs):
+        """ Verify correct operation of DeviceAction.obsoletes. """
+        self.destroyAllDevices(disks=["sdc"])
+        sdc = self.storage.devicetree.getDeviceByName("sdc")
+        self.assertNotEqual(sdc, None)
+
+        sdc1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdc1", parents=[sdc], size=40000)
+
+        # ActionCreateDevice
+        #
+        # - obsoletes other ActionCreateDevice instances w/ lower id and same
+        #   device
+        create_device_1 = ActionCreateDevice(sdc1)
+        create_device_2 = ActionCreateDevice(sdc1)
+        self.assertEqual(create_device_2.obsoletes(create_device_1), True)
+        self.assertEqual(create_device_1.obsoletes(create_device_2), False)
+
+        # ActionCreateFormat
+        #
+        # - obsoletes other ActionCreateFormat instances w/ lower id and same
+        #   device
+        format_1 = self.newFormat("ext3", mountpoint="/home", device=sdc1.path)
+        format_2 = self.newFormat("ext3", mountpoint="/opt", device=sdc1.path)
+        create_format_1 = ActionCreateFormat(sdc1, format_1)
+        create_format_2 = ActionCreateFormat(sdc1, format_2)
+        self.assertEqual(create_format_2.obsoletes(create_format_1), True)
+        self.assertEqual(create_format_1.obsoletes(create_format_2), False)
+
+        # ActionMigrateFormat
+        #
+        # - obsoletes other ActionMigrateFormat instances w/ lower id and same
+        #   device
+        sdc1.format = self.newFormat("ext2", mountpoint="/", device=sdc1.path,
+                                     device_instance=sdc1,
+                                     exists=True)
+        migrate_1 = ActionMigrateFormat(sdc1)
+        migrate_2 = ActionMigrateFormat(sdc1)
+        self.assertEqual(migrate_2.obsoletes(migrate_1), True)
+        self.assertEqual(migrate_1.obsoletes(migrate_2), False)
+
+        # ActionResizeFormat
+        #
+        # - obsoletes other ActionResizeFormat instances w/ lower id and same
+        #   device
+        resize_format_1 = ActionResizeFormat(sdc1, sdc1.size - 1000)
+        resize_format_2 = ActionResizeFormat(sdc1, sdc1.size - 5000)
+        self.assertEqual(resize_format_2.obsoletes(resize_format_1), True)
+        self.assertEqual(resize_format_1.obsoletes(resize_format_2), False)
+
+        # ActionCreateFormat
+        #
+        # - obsoletes migrate, resize format actions w/ lower id on same device
+        new_format = self.newFormat("ext4", mountpoint="/foo", device=sdc1.path)
+        create_format_3 = ActionCreateFormat(sdc1, new_format)
+        self.assertEqual(create_format_3.obsoletes(resize_format_1), True)
+        self.assertEqual(create_format_3.obsoletes(resize_format_2), True)
+        self.assertEqual(create_format_3.obsoletes(migrate_1), True)
+        self.assertEqual(create_format_3.obsoletes(migrate_2), True)
+
+        # ActionResizeDevice
+        #
+        # - obsoletes other ActionResizeDevice instances w/ lower id and same
+        #   device
+        sdc1.exists = True
+        sdc1.format.exists = True
+        resize_device_1 = ActionResizeDevice(sdc1, sdc1.size + 10000)
+        resize_device_2 = ActionResizeDevice(sdc1, sdc1.size - 10000)
+        self.assertEqual(resize_device_2.obsoletes(resize_device_1), True)
+        self.assertEqual(resize_device_1.obsoletes(resize_device_2), False)
+        sdc1.exists = False
+        sdc1.format.exists = False
+
+        # ActionDestroyFormat
+        #
+        # - obsoletes all format actions w/ lower id on same device (including
+        #   self if format does not exist)
+        destroy_format_1 = ActionDestroyFormat(sdc1)
+        self.assertEqual(destroy_format_1.obsoletes(create_format_1), True)
+        self.assertEqual(destroy_format_1.obsoletes(migrate_2), True)
+        self.assertEqual(destroy_format_1.obsoletes(resize_format_1), True)
+        self.assertEqual(destroy_format_1.obsoletes(destroy_format_1), True)
+
+        # ActionDestroyDevice
+        #
+        # - obsoletes all actions w/ lower id that act on the same non-existent
+        #   device (including self)
+        # sdc1 does not exist
+        destroy_sdc1 = ActionDestroyDevice(sdc1)
+        self.assertEqual(destroy_sdc1.obsoletes(create_format_2), True)
+        self.assertEqual(destroy_sdc1.obsoletes(migrate_1), True)
+        self.assertEqual(destroy_sdc1.obsoletes(resize_format_2), True)
+        self.assertEqual(destroy_sdc1.obsoletes(create_device_1), True)
+        self.assertEqual(destroy_sdc1.obsoletes(resize_device_1), True)
+        self.assertEqual(destroy_sdc1.obsoletes(destroy_sdc1), True)
+
+        # ActionDestroyDevice
+        #
+        # - obsoletes all but ActionDestroyFormat actions w/ lower id on the
+        #   same existing device
+        # sda1 exists
+        sda1 = self.storage.devicetree.getDeviceByName("sda1")
+        self.assertNotEqual(sda1, None)
+        resize_sda1_format = ActionResizeFormat(sda1, sda1.size - 50)
+        resize_sda1 = ActionResizeDevice(sda1, sda1.size - 50)
+        destroy_sda1_format = ActionDestroyFormat(sda1)
+        destroy_sda1 = ActionDestroyDevice(sda1)
+        self.assertEqual(destroy_sda1.obsoletes(resize_sda1_format), True)
+        self.assertEqual(destroy_sda1.obsoletes(resize_sda1), True)
+        self.assertEqual(destroy_sda1.obsoletes(destroy_sda1), False)
+        self.assertEqual(destroy_sda1.obsoletes(destroy_sda1_format), False)
+
+    def testActionPruning(self, *args, **kwargs):
+        """ Verify correct functioning of action pruning. """
+        self.destroyAllDevices()
+
+        sda = self.storage.devicetree.getDeviceByName("sda")
+        self.assertNotEqual(sda, None, "failed to find disk 'sda'")
+
+        sda1 = self.newDevice(device_class=PartitionDevice,
+                              name="sda1", size=500, parents=[sda])
+        self.scheduleCreateDevice(device=sda1)
+
+        sda2 = self.newDevice(device_class=PartitionDevice,
+                              name="sda2", size=100000, parents=[sda])
+        self.scheduleCreateDevice(device=sda2)
+        format = self.newFormat("lvmpv", device=sda2.path)
+        self.scheduleCreateFormat(device=sda2, format=format)
+
+        vg = self.newDevice(device_class=LVMVolumeGroupDevice,
+                            name="vg", parents=[sda2])
+        self.scheduleCreateDevice(device=vg)
+
+        lv_root = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                 name="lv_root", vgdev=vg, size=60000)
+        self.scheduleCreateDevice(device=lv_root)
+        format = self.newFormat("ext4", device=lv_root.path, mountpoint="/")
+        self.scheduleCreateFormat(device=lv_root, format=format)
+
+        lv_swap = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                 name="lv_swap", vgdev=vg, size=4000)
+        self.scheduleCreateDevice(device=lv_swap)
+        format = self.newFormat("swap", device=lv_swap.path)
+        self.scheduleCreateFormat(device=lv_swap, format=format)
+
+        # we'll soon schedule destroy actions for these members and the array,
+        # which will test pruning. the whole mess should reduce to nothing
+        sda3 = self.newDevice(device_class=PartitionDevice,
+                              name="sda3", parents=[sda], size=40000)
+        self.scheduleCreateDevice(device=sda3)
+        format = self.newFormat("mdmember", device=sda3.path)
+        self.scheduleCreateFormat(device=sda3, format=format)
+
+        sdb = self.storage.devicetree.getDeviceByName("sdb")
+        self.assertNotEqual(sdb, None, "failed to find disk 'sdb'")
+
+        sdb1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdb1", parents=[sdb], size=40000)
+        self.scheduleCreateDevice(device=sdb1)
+        format = self.newFormat("mdmember", device=sdb1.path,)
+        self.scheduleCreateFormat(device=sdb1, format=format)
+
+        md0 = self.newDevice(device_class=MDRaidArrayDevice,
+                             name="md0", level="raid0", minor=0, size=80000,
+                             memberDevices=2, totalDevices=2,
+                             parents=[sdb1, sda3])
+        self.scheduleCreateDevice(device=md0)
+
+        format = self.newFormat("ext4", device=md0.path, mountpoint="/home")
+        self.scheduleCreateFormat(device=md0, format=format)
+
+        # now destroy the md and its components
+        self.scheduleDestroyFormat(device=md0)
+        self.scheduleDestroyDevice(device=md0)
+        self.scheduleDestroyDevice(device=sdb1)
+        self.scheduleDestroyDevice(device=sda3)
+
+        format = self.newFormat("ext4", mountpoint="/boot", device=sda1.path)
+        self.scheduleCreateFormat(device=sda1, format=format)
+
+        # verify the md actions are present prior to pruning
+        md0_actions = self.storage.devicetree.findActions(devid=md0.id)
+        self.assertNotEqual(len(md0_actions), 0)
+
+        sdb1_actions = self.storage.devicetree.findActions(devid=sdb1.id)
+        self.assertNotEqual(len(sdb1_actions), 0)
+
+        sda3_actions = self.storage.devicetree.findActions(devid=sda3.id)
+        self.assertNotEqual(len(sda3_actions), 0)
+
+        self.storage.devicetree.pruneActions()
+
+        # verify the md actions are gone after pruning
+        md0_actions = self.storage.devicetree.findActions(devid=md0.id)
+        self.assertEqual(len(md0_actions), 0)
+
+        sdb1_actions = self.storage.devicetree.findActions(devid=sdb1.id)
+        self.assertEqual(len(sdb1_actions), 0)
+
+        sda3_actions = self.storage.devicetree.findActions(sda3.id)
+        self.assertEqual(len(sda3_actions), 0)
+
+    def testActionDependencies(self, *args, **kwargs):
+        """ Verify correct functioning of action dependencies. """
+        # ActionResizeDevice
+        # an action that shrinks a device should require the action that
+        # shrinks the device's format
+        lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root")
+        self.assertNotEqual(lv_root, None)
+        shrink_format = ActionResizeFormat(lv_root, lv_root.size - 5000)
+        shrink_device = ActionResizeDevice(lv_root, lv_root.size - 5000)
+        self.assertEqual(shrink_device.requires(shrink_format), True)
+        self.assertEqual(shrink_format.requires(shrink_device), False)
+        shrink_format.cancel()
+        shrink_device.cancel()
+
+        # ActionResizeDevice
+        # an action that grows a format should require the action that
+        # grows the device
+        orig_size = lv_root.currentSize
+        grow_device = ActionResizeDevice(lv_root, orig_size + 100)
+        grow_format = ActionResizeFormat(lv_root, orig_size + 100)
+        self.assertEqual(grow_format.requires(grow_device), True)
+        self.assertEqual(grow_device.requires(grow_format), False)
+
+        # create something like uncommitted autopart
+        self.destroyAllDevices()
+        sda = self.storage.devicetree.getDeviceByName("sda")
+        sdb = self.storage.devicetree.getDeviceByName("sdb")
+        sda1 = self.newDevice(device_class=PartitionDevice,
+                              name="sda1", size=500, parents=[sda])
+        sda1_format = self.newFormat("ext4", mountpoint="/boot",
+                                     device=sda1.path)
+        self.scheduleCreateDevice(device=sda1)
+        self.scheduleCreateFormat(device=sda1, format=sda1_format)
+
+        sda2 = self.newDevice(device_class=PartitionDevice,
+                              name="sda2", size=99500, parents=[sda])
+        sda2_format = self.newFormat("lvmpv", device=sda1.path)
+        self.scheduleCreateDevice(device=sda2)
+        self.scheduleCreateFormat(device=sda2, format=sda2_format)
+
+        sdb1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdb1", size=100000, parents=[sdb])
+        sdb1_format = self.newFormat("lvmpv", device=sdb1.path)
+        self.scheduleCreateDevice(device=sdb1)
+        self.scheduleCreateFormat(device=sdb1, format=sdb1_format)
+
+        vg = self.newDevice(device_class=LVMVolumeGroupDevice,
+                            name="VolGroup", parents=[sda2, sdb1])
+        self.scheduleCreateDevice(device=vg)
+
+        lv_root = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                 name="lv_root", vgdev=vg, size=160000)
+        self.scheduleCreateDevice(device=lv_root)
+        format = self.newFormat("ext4", device=lv_root.path, mountpoint="/")
+        self.scheduleCreateFormat(device=lv_root, format=format)
+
+        lv_swap = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                 name="lv_swap", vgdev=vg, size=4000)
+        self.scheduleCreateDevice(device=lv_swap)
+        format = self.newFormat("swap", device=lv_swap.path)
+        self.scheduleCreateFormat(device=lv_swap, format=format)
+
+        # ActionCreateDevice
+        # creation of an LV should require the actions that create the VG,
+        # its PVs, and the devices that contain the PVs
+        lv_root = self.storage.devicetree.getDeviceByName("VolGroup-lv_root")
+        self.assertNotEqual(lv_root, None)
+        actions = self.storage.devicetree.findActions(type="create",
+                                                      object="device",
+                                                      device=lv_root)
+        self.assertEqual(len(actions), 1,
+                         "wrong number of device create actions for lv_root: "
+                         "%d" % len(actions))
+        create_lv_action = actions[0]
+
+        vgs = [d for d in self.storage.vgs if d.name == "VolGroup"]
+        self.assertNotEqual(vgs, [])
+        vg = vgs[0]
+        actions = self.storage.devicetree.findActions(type="create",
+                                                      object="device",
+                                                      device=vg)
+        self.assertEqual(len(actions), 1,
+                         "wrong number of device create actions for VolGroup")
+        create_vg_action = actions[0]
+
+        self.assertEqual(create_lv_action.requires(create_vg_action), True)
+
+        create_pv_actions = []
+        pvs = [d for d in self.storage.pvs if d in vg.pvs]
+        self.assertNotEqual(pvs, [])
+        for pv in pvs:
+            # include device and format create actions for each pv
+            actions = self.storage.devicetree.findActions(type="create",
+                                                          device=pv)
+            self.assertEqual(len(actions), 2,
+                             "wrong number of device create actions for "
+                             "pv %s" % pv.name)
+            create_pv_actions.append(actions[0])
+
+        for pv_action in create_pv_actions:
+            self.assertEqual(create_lv_action.requires(pv_action), True)
+            # also check that the vg create action requires the pv actions
+            self.assertEqual(create_vg_action.requires(pv_action), True)
+
+        # ActionCreateDevice
+        # the higher numbered partition of two that are scheduled to be
+        # created on a single disk should require the action that creates the
+        # lower numbered of the two, eg: create sda2 before creating sda3
+        sdc = self.storage.devicetree.getDeviceByName("sdc")
+        self.assertNotEqual(sdc, None)
+
+        sdc1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdc1", parents=[sdc], size=50000)
+        create_sdc1 = self.scheduleCreateDevice(device=sdc1)
+        self.assertEqual(isinstance(create_sdc1, ActionCreateDevice), True)
+
+        sdc2 = self.newDevice(device_class=PartitionDevice,
+                              name="sdc2", parents=[sdc], size=50000)
+        create_sdc2 = self.scheduleCreateDevice(device=sdc2)
+        self.assertEqual(isinstance(create_sdc2, ActionCreateDevice), True)
+
+        self.assertEqual(create_sdc2.requires(create_sdc1), True)
+        self.assertEqual(create_sdc1.requires(create_sdc2), False)
+
+        # ActionCreateDevice
+        # actions that create partitions on two separate disks should not
+        # require each other, regardless of the partitions' numbers
+        sda1 = self.storage.devicetree.getDeviceByName("sda1")
+        self.assertNotEqual(sda1, None)
+        actions = self.storage.devicetree.findActions(type="create",
+                                                      object="device",
+                                                      device=sda1)
+        self.assertEqual(len(actions), 1,
+                         "wrong number of create actions found for sda1")
+        create_sda1 = actions[0]
+        self.assertEqual(create_sdc2.requires(create_sda1), False)
+        self.assertEqual(create_sda1.requires(create_sdc1), False)
+
+        # ActionDestroyDevice
+        # an action that destroys a device containing an mdmember format
+        # should require the action that destroys the md array it is a
+        # member of if an array is defined
+        self.destroyAllDevices(disks=["sdc", "sdd"])
+        sdc = self.storage.devicetree.getDeviceByName("sdc")
+        self.assertNotEqual(sdc, None)
+        sdd = self.storage.devicetree.getDeviceByName("sdd")
+        self.assertNotEqual(sdd, None)
+
+        sdc1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdc1", parents=[sdc], size=40000)
+        self.scheduleCreateDevice(device=sdc1)
+        format = self.newFormat("mdmember", device=sdc1.path)
+        self.scheduleCreateFormat(device=sdc1, format=format)
+
+        sdd1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdd1", parents=[sdd], size=40000)
+        self.scheduleCreateDevice(device=sdd1)
+        format = self.newFormat("mdmember", device=sdd1.path,)
+        self.scheduleCreateFormat(device=sdd1, format=format)
+
+        md0 = self.newDevice(device_class=MDRaidArrayDevice,
+                             name="md0", level="raid0", minor=0, size=80000,
+                             memberDevices=2, totalDevices=2,
+                             parents=[sdc1, sdd1])
+        self.scheduleCreateDevice(device=md0)
+        format = self.newFormat("ext4", device=md0.path, mountpoint="/home")
+        self.scheduleCreateFormat(device=md0, format=format)
+
+        destroy_md0_format = self.scheduleDestroyFormat(device=md0)
+        destroy_md0 = self.scheduleDestroyDevice(device=md0)
+        destroy_members = [self.scheduleDestroyDevice(device=sdc1)]
+        destroy_members.append(self.scheduleDestroyDevice(device=sdd1))
+
+        for member in destroy_members:
+            # device and format destroy actions for md members should require
+            # both device and format destroy actions for the md array
+            for array in [destroy_md0_format, destroy_md0]:
+                self.assertEqual(member.requires(array), True)
+
+        # ActionDestroyDevice
+        # when there are two actions that will each destroy a partition on the
+        # same disk, the action that will destroy the lower-numbered
+        # partition should require the action that will destroy the higher-
+        # numbered partition, eg: destroy sda2 before destroying sda1
+        self.destroyAllDevices(disks=["sdc", "sdd"])
+        sdc1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdc1", parents=[sdc], size=50000)
+        self.scheduleCreateDevice(device=sdc1)
+
+        sdc2 = self.newDevice(device_class=PartitionDevice,
+                              name="sdc2", parents=[sdc], size=40000)
+        self.scheduleCreateDevice(device=sdc2)
+
+        destroy_sdc1 = self.scheduleDestroyDevice(device=sdc1)
+        destroy_sdc2 = self.scheduleDestroyDevice(device=sdc2)
+        self.assertEqual(destroy_sdc1.requires(destroy_sdc2), True)
+        self.assertEqual(destroy_sdc2.requires(destroy_sdc1), False)
+
+        self.destroyAllDevices(disks=["sdc", "sdd"])
+        sdc = self.storage.devicetree.getDeviceByName("sdc")
+        self.assertNotEqual(sdc, None)
+        sdd = self.storage.devicetree.getDeviceByName("sdd")
+        self.assertNotEqual(sdd, None)
+
+        sdc1 = self.newDevice(device_class=PartitionDevice,
+                              name="sdc1", parents=[sdc], size=50000)
+        create_pv = self.scheduleCreateDevice(device=sdc1)
+        format = self.newFormat("lvmpv", device=sdc1.path)
+        create_pv_format = self.scheduleCreateFormat(device=sdc1, format=format)
+
+        testvg = self.newDevice(device_class=LVMVolumeGroupDevice,
+                                name="testvg", parents=[sdc1], size=50000)
+        create_vg = self.scheduleCreateDevice(device=testvg)
+        testlv = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                name="testlv", vgdev=testvg, size=30000)
+        create_lv = self.scheduleCreateDevice(device=testlv)
+        format = self.newFormat("ext4", device=testlv.path)
+        create_lv_format = self.scheduleCreateFormat(device=testlv, format=format)
+
+        # ActionCreateFormat
+        # creation of a format on a non-existent device should require the
+        # action that creates the device
+        self.assertEqual(create_lv_format.requires(create_lv), True)
+
+        # ActionCreateFormat
+        # an action that creates a format on a device should require an action
+        # that creates a device that the format's device depends on
+        self.assertEqual(create_lv_format.requires(create_pv), True)
+        self.assertEqual(create_lv_format.requires(create_vg), True)
+
+        # ActionCreateFormat
+        # an action that creates a format on a device should require an action
+        # that creates a format on a device that the format's device depends on
+        self.assertEqual(create_lv_format.requires(create_pv_format), True)
+
+        # XXX from here on, the devices are existing but not in the tree, so
+        #     we instantiate and use actions directly
+        self.destroyAllDevices(disks=["sdc", "sdd"])
+        sdc1 = self.newDevice(device_class=PartitionDevice, exists=True,
+                              name="sdc1", parents=[sdc], size=50000)
+        sdc1.format = self.newFormat("lvmpv", device=sdc1.path, exists=True,
+                                     device_instance=sdc1)
+        testvg = self.newDevice(device_class=LVMVolumeGroupDevice, exists=True,
+                                name="testvg", parents=[sdc1], size=50000)
+        testlv = self.newDevice(device_class=LVMLogicalVolumeDevice,
+                                exists=True,
+                                name="testlv", vgdev=testvg, size=30000)
+        testlv.format = self.newFormat("ext4", device=testlv.path,
+                                       exists=True, device_instance=testlv)
+
+        # ActionResizeDevice
+        # an action that resizes a device should require an action that grows
+        # a device that the first action's device depends on, eg: grow
+        # device containing PV before resize of VG or LVs
+        tmp = sdc1.format
+        sdc1.format = None      # since lvmpv format is not resizable
+        grow_pv = ActionResizeDevice(sdc1, sdc1.size + 10000)
+        grow_lv = ActionResizeDevice(testlv, testlv.size + 5000)
+        grow_lv_format = ActionResizeFormat(testlv, testlv.size + 5000)
+
+        self.assertEqual(grow_lv.requires(grow_pv), True)
+        self.assertEqual(grow_pv.requires(grow_lv), False)
+
+        # ActionResizeFormat
+        # an action that grows a format should require the action that grows
+        # the format's device
+        self.assertEqual(grow_lv_format.requires(grow_lv), True)
+        self.assertEqual(grow_lv.requires(grow_lv_format), False)
+
+        # ActionResizeFormat
+        # an action that resizes a device's format should depend on an action
+        # that grows a device the first device depends on
+        self.assertEqual(grow_lv_format.requires(grow_pv), True)
+        self.assertEqual(grow_pv.requires(grow_lv_format), False)
+
+        # ActionResizeFormat
+        # an action that resizes a device's format should depend on an action
+        # that grows a format on a device the first device depends on
+        # XXX resize of PV format is not allowed, so there's no real-life
+        #     example of this to test
+
+        grow_lv_format.cancel()
+        grow_lv.cancel()
+        grow_pv.cancel()
+
+        # ActionResizeDevice
+        # an action that resizes a device should require an action that grows
+        # a format on a device that the first action's device depends on, eg:
+        # grow PV format before resize of VG or LVs
+        # XXX resize of PV format is not allowed, so there's no real-life
+        #     example of this to test
+
+        # ActionResizeDevice
+        # an action that resizes a device should require an action that
+        # shrinks a device that depends on the first action's device, eg:
+        # shrink LV before resizing VG or PV devices
+        shrink_lv = ActionResizeDevice(testlv, testlv.size - 10000)
+        shrink_pv = ActionResizeDevice(sdc1, sdc1.size - 5000)
+
+        self.assertEqual(shrink_pv.requires(shrink_lv), True)
+        self.assertEqual(shrink_lv.requires(shrink_pv), False)
+
+        # ActionResizeDevice
+        # an action that resizes a device should require an action that
+        # shrinks a format on a device that depends on the first action's
+        # device, eg: shrink LV format before resizing VG or PV devices
+        shrink_lv_format = ActionResizeFormat(testlv, testlv.size)
+        self.assertEqual(shrink_pv.requires(shrink_lv_format), True)
+        self.assertEqual(shrink_lv_format.requires(shrink_pv), False)
+
+        # ActionResizeFormat
+        # an action that resizes a device's format should depend on an action
+        # that shrinks a device that depends on the first device
+        # XXX can't think of a real-world example of this since PVs and MD
+        #     member devices are not resizable in anaconda
+
+        # ActionResizeFormat
+        # an action that resizes a device's format should depend on an action
+        # that shrinks a format on a device that depends on the first device
+        # XXX can't think of a real-world example of this since PVs and MD
+        #     member devices are not resizable in anaconda
+
+        shrink_lv_format.cancel()
+        shrink_lv.cancel()
+        shrink_pv.cancel()
+        sdc1.format = tmp   # restore pv's lvmpv format
+
+        # ActionCreateFormat
+        # an action that creates a format on a device should require an action
+        # that resizes a device that the format's device depends on
+        # XXX Really? Is this always so?
+
+        # ActionCreateFormat
+        # an action that creates a format on a device should require an action
+        # that resizes a format on a device that the format's device depends on
+        # XXX Same as above.
+
+        # ActionCreateFormat
+        # an action that creates a format on a device should require an action
+        # that resizes the device that will contain the format
+        grow_lv = ActionResizeDevice(testlv, testlv.size + 1000)
+        format = self.newFormat("msdos", device=testlv.path)
+        format_lv = ActionCreateFormat(testlv, format)
+        self.assertEqual(format_lv.requires(grow_lv), True)
+        self.assertEqual(grow_lv.requires(format_lv), False)
+
+        # ActionDestroyFormat
+        # an action that destroys a format should require an action that
+        # destroys a device that depends on the format's device
+        destroy_pv_format = ActionDestroyFormat(sdc1)
+        destroy_lv_format = ActionDestroyFormat(testlv)
+        destroy_lv = ActionDestroyDevice(testlv)
+        self.assertEqual(destroy_pv_format.requires(destroy_lv), True)
+        self.assertEqual(destroy_lv.requires(destroy_pv_format), False)
+
+        # ActionDestroyFormat
+        # an action that destroys a format should require an action that
+        # destroys a format on a device that depends on the first format's
+        # device
+        self.assertEqual(destroy_pv_format.requires(destroy_lv_format), True)
+        self.assertEqual(destroy_lv_format.requires(destroy_pv_format), False)
+
+    def testActionSorting(self, *args, **kwargs):
+        """ Verify correct functioning of action sorting. """
+        pass
+
+
+def suite():
+    return unittest.TestLoader().loadTestsFromTestCase(DeviceActionTestCase)
+
+
+if __name__ == "__main__":
+    unittest.main()
+
-- 
1.7.2.3


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]