#!/usr/bin/python import unittest from mock import Mock from mock import TestCase from storagetestcase import StorageTestCase 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 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()