* storage/devices.py (DMRaidArrayDevice): Complete the DMRaidArrayDevice class. * storage/devices.py (PartitionDeviceFactory): When creating a partition we need to decide, based on the device holding the partition, what type of partition class will be returned. * storage/devices.py (PartitionDevice): When setting the disk we must be careful to change stuff accordingly. * storage/devicetree.py (addUdevDevice): Handle the creation of the dmraid device and its members in the addUdeveDevice function. * storage/formats/dmraid.py (DMRaidMember): Complete the format that handles devices being members of a dmraid array. * storage/udev.py (udev_device_is_dmraid): add a function that recognizes dmraid devices from the provided info list. * storage/udev.py (udev_device_get_dmraid_partition_disk): function that gets the disk string from partition info. * storage/udev.py (udev_device_is_dmraid_partition): function that checks for dmraid partitions. * storage/__init__.py (disks(self)): return a list of disks from the internal attribute. * storage/__init__.py (Storage.disks): we really want to make sure we return all the objects that comply with the DiskDevice interface (inheret from DiskDevice) * storage/__init__.py (Storage.partitions): Same as above but for PartitionDevice. * iw/autopart_type (PartitionTypeWindow(InstallWindow)): use disk.name instead of munging the first 5 characters of the path. * storage/partitioning (newPartition): Use the new partition factory to create a partition. --- iw/autopart_type.py | 9 +-- iw/partition_gui.py | 8 +- storage/__init__.py | 22 ++++- storage/devices.py | 202 +++++++++++++++++++++++++++++++++++++++++---- storage/devicetree.py | 92 +++++++++++++++++--- storage/formats/dmraid.py | 23 ++++-- storage/partitioning.py | 4 +- storage/udev.py | 33 ++++++++ 8 files changed, 341 insertions(+), 52 deletions(-) diff --git a/iw/autopart_type.py b/iw/autopart_type.py index ba8c0dc..700d7da 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -381,14 +381,11 @@ class PartitionTypeWindow(InstallWindow): else: defaultBoot = None for disk in self.storage.disks: - partedDisk = disk.partedDisk - if partedDisk.device.path[5:] not in self.anaconda.id.bootloader.drivelist: + if disk.name not in self.anaconda.id.bootloader.drivelist: continue - size = partedDisk.device.getSize(unit="MB") - dispstr = "%s %8.0f MB %s" %(partedDisk.device.path[5:], - size, partedDisk.device.model) + dispstr = "%s %8.0f MB %s" %(disk.name, disk.size, disk.partedDisk.device.model) i = bootstore.append(None) - bootstore[i] = (dispstr, partedDisk.device.path[5:]) + bootstore[i] = (dispstr, disk.name) if disk.name == defaultBoot: self.bootcombo.set_active_iter(i) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index c704b08..9dae436 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -142,12 +142,12 @@ class DiskStripeSlice: if self.partition.type & parted.PARTITION_FREESPACE: rc = "Free\n" else: - rc = "%s\n" % (self.partition.getDeviceNodeName(),) + rc = "%s\n" % (self.partition.getDeviceNodeName().split("/")[-1],) rc = rc + "%Ld MB" % (self.partition.getSize(unit="MB"),) return rc def getDeviceName(self): - return self.partition.getDeviceNodeName() + return self.partition.getDeviceNodeName().split("/")[-1] def update(self): disk = self.parent.getDisk() @@ -808,7 +808,7 @@ class PartitionWindow(InstallWindow): part = part.nextPartition() continue - partName = part.getDeviceNodeName() + partName = part.getDeviceNodeName().split("/")[-1] device = self.storage.devicetree.getDeviceByName(partName) if not device and not part.type & parted.PARTITION_FREESPACE: raise RuntimeError("can't find partition %s in device" @@ -1054,7 +1054,7 @@ class PartitionWindow(InstallWindow): self.editLVMVolumeGroup(device) elif device.type == "lvmlv": self.editLVMVolumeGroup(device) - elif device.type == "partition": + elif isinstance(device, storage.devices.PartitionDevice): self.editPartition(device) # isNew implies that this request has never been successfully used before diff --git a/storage/__init__.py b/storage/__init__.py index 7bac367..6c241c2 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -238,7 +238,11 @@ class Storage(object): does not necessarily reflect the actual on-disk state of the system's disks. """ - disks = self.devicetree.getDevicesByType("disk") + disks = [] + devices = self.devicetree.devices + for d in devices: + if isinstance(devices[d], DiskDevice): + disks.append(devices[d]) disks.sort(key=lambda d: d.name) return disks @@ -250,7 +254,11 @@ class Storage(object): does not necessarily reflect the actual on-disk state of the system's disks. """ - partitions = self.devicetree.getDevicesByType("partition") + partitions = [] + devices = self.devicetree.devices + for d in devices: + if isinstance(devices[d], PartitionDevice): + partitions.append(devices[d]) partitions.sort(key=lambda d: d.name) return partitions @@ -423,7 +431,7 @@ class Storage(object): if device.name in self.protectedPartitions: return _("This partition is holding the data for the hard " "drive install.") - elif device.type == "partition" and device.isProtected: + elif isinstance(device, PartitionDevice) and device.isProtected: # LDL formatted DASDs always have one partition, you'd have to # reformat the DASD in CDL mode to get rid of it return _("You cannot delete a partition of a LDL formatted " @@ -445,7 +453,7 @@ class Storage(object): else: return _("This device is part of a LVM volume " "group.") - elif device.type == "partition" and device.isExtended: + elif isinstance(device, PartitionDevice) and device.isExtended: reasons = {} for dep in self.deviceDeps(device): reason = self.deviceImmutable(dep) @@ -473,13 +481,17 @@ class Storage(object): if kwargs.has_key("disks"): parents = kwargs.pop("disks") + if isinstance(parents, Device): + kwargs["parents"] = [parents] + else: + kwargs["parents"] = parents if kwargs.has_key("name"): name = kwargs.pop("name") else: name = "req%d" % self.nextID - return PartitionDevice(name, *args, **kwargs) + return PartitionDeviceFactory(name, *args, **kwargs) def newMDArray(self, *args, **kwargs): """ Return a new MDRaidArrayDevice instance for configuring. """ diff --git a/storage/devices.py b/storage/devices.py index 84cfe1b..dd5a4f1 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -127,6 +127,74 @@ def get_device_majors(): return majors device_majors = get_device_majors() +def PartitionDeviceFactory(*args, **kwargs): + """This will decide what PartitionDevice class will be used. + + Arguments: + + name -- the device name (generally a device node's basename) + + Keyword Arguments: + + exists -- indicates whether this is an existing device + format -- the device's format (DeviceFormat instance) + + For existing partitions: + + parents -- the disk that contains this partition + major -- the device major + minor -- the device minor + sysfsPath -- sysfs device path + + For new partitions: + + partType -- primary,extended,&c (as parted constant) + grow -- whether or not to grow the partition + maxsize -- max size for growable partitions (in MB) + size -- the device's size (in MB) + bootable -- whether the partition is bootable + parents -- a list of potential containing disks + + The decision will be made base on finding the disk by name + """ + # FIXME: PRePBootDevice should be here somehow.!!! + roots = ["/dev", "/dev/mapper"] + # firs lets normalize the name + norm_name = args[0].split("/")[-1] + + # We look for the disk in /dev. From PartitionDevice its the [0] one. + if (not kwargs.has_key("parents")) or\ + (kwargs.has_key("exists") and not kwargs["exists"]): + # Cant really choose a good type of class, default to PartitionDevice + # This will be considered as a request. + return PartitionDevice(*args, **kwargs) + else: + parents = kwargs["parents"] + if isinstance(parents, Device): + parents = [parents] + # we receive a list of parents. look for the disk that contains the name + # if we dont find the name we will return PartitionDevice and let it raise + # the exception. + for root in roots: + for parent in parents: + path = os.path.join(root,norm_name) + log.debug("looking up parted Partition: %s" % path) + part = parent.partedDisk.getPartitionByPath(path) + if not part: + continue + else: + # we have found a part. no make the decision based on path + if path.startswith(roots[1]): + #we create a DMPartition + return DMRaidPartitionDevice(*args, **kwargs) + elif path.startswith(roots[0]): + # make sure that the parent is consistent + kwargs["parents"] = [parent] + return PartitionDevice(*args, **kwargs) + + # If we get here, we did not find anything. + return PartitionDevice(*args, **kwargs) + class Device(object): """ A generic device. @@ -861,14 +929,14 @@ class PartitionDevice(StorageDevice): # no need to clobber our name else: self._partedPartition = partition - self._name = partition.getDeviceNodeName() + self._name = partition.getDeviceNodeName().split("/")[-1] partedPartition = property(lambda d: d._getPartedPartition(), lambda d,p: d._setPartedPartition(p)) def dependsOn(self, dep): """ Return True if this device depends on dep. """ - if dep.type == "partition" and dep.isExtended and self.isLogical: + if isinstance(dep, PartitionDevice) and dep.isExtended and self.isLogical: return True return Device.dependsOn(self, dep) @@ -1051,7 +1119,7 @@ class PartitionDevice(StorageDevice): geometry.length = new_length def _getDisk(self): - """ The disk that contains this partition. """ + """ The disk that contains this partition.""" try: disk = self.parents[0] except IndexError: @@ -1059,12 +1127,27 @@ class PartitionDevice(StorageDevice): return disk def _setDisk(self, disk): + """Change the parent. + + Setting up a disk is not trivial. It has the potential to change + the underlying object. If necessary we must also change this object. + """ log_method_call(self, self.name, old=self.disk, new=disk) if self.disk: self.disk.removeChild() self.parents = [disk] disk.addChild() + if isinstance(disk, DMRaidArrayDevice): + # modify the partition so it can look like the DMRaidPartitionDevice. + + self._type = "dmraid partition" + self._packages = ["dmraid"] + self.devDir = "/dev/mapper" + self.updateSysfsPath = DMDevice.updateSysfsPath + self.getDMNode = DMDevice.getDMNode + + disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d)) @@ -2069,41 +2152,130 @@ class MDRaidArrayDevice(StorageDevice): self.exists = False -class DMRaidArrayDevice(DMDevice): +class DMRaidArrayDevice(DiskDevice): """ A dmraid (device-mapper RAID) device """ _type = "dm-raid array" _packages = ["dmraid"] + devDir = "/dev/mapper" - def __init__(self, name, format=None, size=None, - exists=None, parents=None, sysfsPath=''): + def __init__(self, name, raidSet=None, level=None, format=None, size=None, + major=None, minor=None, parents=None, sysfsPath=''): """ Create a DMRaidArrayDevice instance. Arguments: - name -- the device name (generally a device node's basename) + name -- the dmraid name also the device node's basename Keyword Arguments: + raidSet -- the RaidSet object from block + level -- the type of dmraid parents -- a list of the member devices sysfsPath -- sysfs device path size -- the device's size format -- a DeviceFormat instance - exists -- indicates whether this is an existing device """ if isinstance(parents, list): for parent in parents: if not parent.format or parent.format.type != "dmraidmember": raise ValueError("parent devices must contain dmraidmember format") - DMDevice.__init__(self, name, format=format, size=size, - parents=parents, sysfsPath=sysfsPath, - exists=exists) + DiskDevice.__init__(self, name, format=format, size=size, + major=major, minor=minor, + parents=parents, sysfsPath=sysfsPath) - def probe(self): - """ Probe for any missing information about this device. + self.formatClass = get_device_format_class("dmraidmember") + if not self.formatClass: + raise DeviceError("cannot find class for 'dmraidmember'") - size + + self._raidSet = raidSet + self._level = level + + @property + def raidSet(self): + return self._raidSet + + @raidSet.setter + def raidSet(self, val): + # If we change self._raidSet, parents list will be invalid. + # Don't allow the change. + pass + + def _addDevice(self, device): + """ Add a new member device to the array. + + XXX This is for use when probing devices, not for modification + of arrays. """ - raise NotImplementedError("probe method not defined for DMRaidArrayDevice") + log_method_call(self, self.name, device=device.name, status=self.status) + + if not self.exists: + raise DeviceError("device has not been created") + + if not isinstance(device.format, self.formatClass): + raise ValueError("invalid device format for dmraid member") + + if device in self.members: + raise ValueError("device is already a member of this array") + + # we added it, so now set up the relations + self.devices.append(device) + device.addChild() + + @property + def members(self): + return self.parents + + @property + def devices(self): + """ Return a list of this array's member device instances. """ + return self.parents + + def activate(self, mknod=True): + self._raidSet.activate(mknod=mknod) + + def deactivate(self): + self._raidSet.deactivate() + + # We are more like a disk then a dmdevice, but we are still a dmdevice, + # so we need to "inherit" some functions from dmdevice + def updateSysfsPath(self): + DMDevice.updateSysfsPath(self) + + def getDMNode(self): + DMDevice.getDMNode(self) + + def teardown(self, recursive=None): + # avoid DiskDevice's overriding of teardown() + StorageDevice.teardown(self, recursive) + + +class DMRaidPartitionDevice(PartitionDevice): + """ A disk partition in a dmraid array, identical to a general + PartitionDevice, except for the device path and sysfs path + """ + _type = "dmraid partition" + _packages = ["dmraid"] + devDir = "/dev/mapper" + + def __init__(self, name, format=None, + size=None, grow=False, maxsize=None, + major=None, minor=None, bootable=None, + sysfsPath='', parents=None, exists=None, + partType=None, primary=False): + PartitionDevice.__init__(self, name, format=format, size=size, + major=major, minor=minor, bootable=bootable, + sysfsPath=sysfsPath, parents=parents, + exists=exists, partType=partType, + primary=primary) + + # We are more like a partition then a dmdevice, but we are still a dmdevice + # so we need to "inherit" some functions from dmdevice + def updateSysfsPath(self): + DMDevice.updateSysfsPath(self) + + def getDMNode(self): + DMDevice.getDMNode(self) class MultipathDevice(DMDevice): diff --git a/storage/devicetree.py b/storage/devicetree.py index 3494d5a..a83279d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -21,6 +21,7 @@ # import os +import block from errors import * from devices import * @@ -458,7 +459,7 @@ class DeviceTree(object): raise ValueError("Cannot remove non-leaf device '%s'" % dev.name) # if this is a partition we need to remove it from the parted.Disk - if dev.type == "partition": + if isinstance(dev, PartitionDevice): dev.disk.removePartition(dev) self._devices.remove(dev) @@ -558,9 +559,9 @@ class DeviceTree(object): # special handling for extended partitions since the logical # partitions and their deps effectively depend on the extended logicals = [] - if dep.type == "partition" and dep.isExtended: + if isinstance(dep, PartitionDevice): # collect all of the logicals on the same disk - for part in self.getDevicesByType("partition"): + for part in self.getDevicesByInstance(PartitionDevice): if part.isLogical and part.disk == dep.disk: logicals.append(part) @@ -601,6 +602,13 @@ class DeviceTree(object): # this is a partition on a disk in the ignore list return True + # Ignore partitions found on the raw disks which are part of a + # dmraidset + for set in self.getDevicesByType("dm-raid array"): + for disk in set.parents: + if disk.name == os.path.basename(os.path.dirname(sysfs_path)): + return True + # FIXME: check for virtual devices whose slaves are on the ignore list def addUdevDevice(self, info): @@ -667,6 +675,19 @@ class DeviceTree(object): log.error("failure scanning device %s" % name) return + if device is None and \ + udev_device_is_dmraid_partition(info, self): + diskname = udev_device_get_dmraid_partition_disk(info) + disk = self.getDeviceByName(diskname) + device = PartitionDeviceFactory(name, \ + sysfsPath=sysfs_path, \ + major=udev_device_get_major(info), \ + minor=udev_device_get_minor(info), \ + exists=True, \ + parents=[disk]) + self._addDevice(device) + #self.ignoredDisks.append(name) + # if we get here, we found all of the slave devices and # something must be wrong -- if all of the slaves are in # the tree, this device should be as well @@ -741,6 +762,17 @@ class DeviceTree(object): minor=udev_device_get_minor(info), sysfsPath=sysfs_path) self._addDevice(device) + elif udev_device_is_dmraid(info): + # This is just temporary as I need to differentiate between the + # device that has partitions and device that dont. + log.debug("%s is part of a dmraid" % name) + device = self.getDeviceByName(name) + if device is None: + device = StorageDevice(name, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + sysfsPath=sysfs_path, exists=True) + self._addDevice(device) elif udev_device_is_disk(info): log.debug("%s is a disk" % name) device = self.getDeviceByName(name) @@ -793,12 +825,12 @@ class DeviceTree(object): log.error("failure scanning device %s" % disk_name) return - device = PartitionDevice(name, - sysfsPath=sysfs_path, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - exists=True, - parents=[disk]) + device = PartitionDeviceFactory(name, + sysfsPath=sysfs_path, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + exists=True, + parents=[disk]) self._addDevice(device) # @@ -824,9 +856,8 @@ class DeviceTree(object): except KeyError: log.debug("mdraid member %s has no md uuid" % name) elif format_type == "isw_raid_member": - # dmraid - # TODO: collect name of containing raidset - # TODO: implement dmraid member format class + # We dont add any new args because we intend to use the same + # block.RaidSet object for all the related devices. pass elif format_type == "LVM2_member": # lvm @@ -914,8 +945,38 @@ class DeviceTree(object): parents=[device]) self._addDevice(md_array) elif format.type == "dmraidmember": - # look up or create the dmraid array - pass + major = udev_device_get_major(info) + minor = udev_device_get_minor(info) + # Have we already created the DMRaidArrayDevice? + rs = block.getRaidSetFromRelatedMem(uuid=uuid, name=name, + major=major, minor=minor) + if rs is None: + # FIXME: Should handle not finding a dmriad dev better + pass + + dm_array = self.getDeviceByName(rs.name) + if dm_array is not None: + # We add the new device. + dm_array._addDevice(device) + else: + # Activate the Raid set. + rs.activate(mknod=True) + + # Create the DMRaidArray + dm_array = DMRaidArrayDevice(rs.name, + major=major, minor=minor, + raidSet=rs, + level=rs.level, + parents=[device]) + + self._addDevice(dm_array) + + # Use the rs's object on the device. + # pyblock can return the memebers of a set and the device has + # the attribute to hold it. But ATM we are not really using it. + # Commenting this out until we really need it. + #device.format.raidmem = block.getMemFromRaidSet(dm_array, + # major=major, minor=minor, uuid=uuid, name=name) elif format.type == "lvmpv": # lookup/create the VG and LVs try: @@ -1077,6 +1138,9 @@ class DeviceTree(object): # TODO: expand this to catch device format types return [d for d in self._devices if d.type == device_type] + def getDevicesByInstance(self, device_class): + return [d for d in self._devices if isinstance(d, device_class)] + @property def devices(self): """ Dict with device path keys and Device values. """ diff --git a/storage/formats/dmraid.py b/storage/formats/dmraid.py index d458b45..ef80902 100644 --- a/storage/formats/dmraid.py +++ b/storage/formats/dmraid.py @@ -20,6 +20,8 @@ # Red Hat Author(s): Dave Lehman <dlehman@xxxxxxxxxx> # +import block + from iutil import log_method_call #from dm import dm_node_from_name from ..errors import * @@ -65,25 +67,34 @@ class DMRaidMember(DeviceFormat): device -- path to the underlying device uuid -- this format's UUID - raidSet -- the name of the raidset this member belongs to exists -- indicates whether this is an existing format + On initialization this format is like DeviceFormat + """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs) - - self.raidSet = kwargs.get("raidSet") + + # Initialize the attribute that will hold the block object. + self._raidmem = None + + @property + def raidmem(self): + return self._raidmem + + @raidmem.setter + def raidmem(self, raidmem): + self._raidmem = raidmem def create(self, *args, **kwargs): log_method_call(self, device=self.device, type=self.type, status=self.status) - raise DMRaidMemberError("creation of dmraid members is not supported") + raise DMRaidMemberError("creation of dmraid members is non-sense") def destroy(self, *args, **kwargs): log_method_call(self, device=self.device, type=self.type, status=self.status) - raise DMRaidMemberError("destruction of dmraid members is not supported") - + raise DMRaidMemberError("destruction of dmraid members is non-sense") register_device_format(DMRaidMember) diff --git a/storage/partitioning.py b/storage/partitioning.py index d1f2d4d..78929cf 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -32,7 +32,7 @@ from constants import * from errors import * from deviceaction import * -from devices import PartitionDevice, LUKSDevice +from devices import PartitionDeviceFactory, LUKSDevice import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -530,7 +530,7 @@ def doPartitioning(storage, exclusiveDisks=None): # that does not exist means leaving self.parents empty and instead # populating self.req_disks. In this case, we need to skip past # that since this partition is already defined. - device = PartitionDevice(extended.getDeviceNodeName(), + device = PartitionDeviceFactory(extended.getDeviceNodeName(), parents=disk) device.parents = [disk] device.partedPartition = extended diff --git a/storage/udev.py b/storage/udev.py index 6df00c8..a39b98c 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -270,4 +270,37 @@ def udev_device_get_lv_sizes(info): return [float(s) / 1024 for s in sizes] +def udev_device_is_dmraid(info): + # Note that this function does *not* identify raid sets. + # Tests to see if device is parto of a dmraid set. + # dmraid and mdriad have the same ID_FS_USAGE string, ID_FS_TYPE has a + # string that describes the type of dmraid (isw_raid_member...), I don't + # want to maintain a list and mdraid's ID_FS_TYPE='linux_raid_member', so + # dmraid will be everything that is raid and not linux_raid_member + if info.has_key("ID_FS_USAGE") and info.has_key("ID_FS_TYPE") and \ + info["ID_FS_USAGE"] == "raid" and \ + info["ID_FS_TYPE"] != "linux_raid_member": + return True + + return False + +def udev_device_get_dmraid_partition_disk(info): + try: + p_index = info["DM_NAME"].rindex("p") + except: + return None + + if not info["DM_NAME"][p_index+1:].isdigit(): + return None + + return info["DM_NAME"][:p_index] + +def udev_device_is_dmraid_partition(info, devicetree): + diskname = udev_device_get_dmraid_partition_disk(info) + dmraid_devices = devicetree.getDevicesByType("dm-raid array") + + for device in dmraid_devices: + if diskname == device.name: + return True + return False -- 1.6.0.6 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list