This makes DeviceTree.populate()'s task simpler by providing the devices in the same order udev reports them so we do not have to iterate through the device list so many times (and force skipping some of the devices during the first go). I also hope the methods' input/output is clearer now. --- pyanaconda/iw/filter_gui.py | 24 ++++---- pyanaconda/storage/devicelibs/mpath.py | 102 ++++++++++++++++++++++++++++++++ pyanaconda/storage/devicetree.py | 12 +--- 3 files changed, 119 insertions(+), 19 deletions(-) diff --git a/pyanaconda/iw/filter_gui.py b/pyanaconda/iw/filter_gui.py index 76f0a72..cca6581 100644 --- a/pyanaconda/iw/filter_gui.py +++ b/pyanaconda/iw/filter_gui.py @@ -33,7 +33,8 @@ from pyanaconda.constants import * from iw_gui import * from pyanaconda.storage.devices import devicePathToName from pyanaconda.storage.udev import * -from pyanaconda.storage.devicelibs.mpath import * +from pyanaconda.storage.devicelibs.mpath\ + import DeviceTopology, MultipathConfigWriter from pyanaconda.flags import flags from pyanaconda.storage import iscsi from pyanaconda.storage import fcoe @@ -409,7 +410,7 @@ class FilterWindow(InstallWindow): return True def _getFilterDisks(self): - """ Return a list of disks to pass to identifyMultipaths. """ + """ Return a list of disks to pass to DeviceTopology. """ return filter(lambda d: udev_device_is_disk(d) and \ not udev_device_is_dm(d) and \ not udev_device_is_md(d) and \ @@ -451,17 +452,17 @@ class FilterWindow(InstallWindow): del cfg del mcw - (new_singlepaths, new_mpaths, new_partitions) = identifyMultipaths(new_disks) + topology = DeviceTopology(new_disks) (new_raids, new_nonraids) = self.split_list(lambda d: isRAID(d) and not isCCISS(d), - new_singlepaths) + topology.singlepaths_iter()) nonraids = filter(lambda d: d not in self._cachedDevices, new_nonraids) raids = filter(lambda d: d not in self._cachedRaidDevices, new_raids) # The end result of the loop below is that mpaths is a list of lists of - # components, just like new_mpaths. That's what populate expects. + # components. That's what populate expects. mpaths = [] - for mp in new_mpaths: + for mp in topology.multipaths_iter(): for d in mp: # If all components of this multipath device are in the # cache, skip it. Otherwise, it's a new device and needs to @@ -601,12 +602,13 @@ class FilterWindow(InstallWindow): open("/etc/multipath.conf", "w+").write(cfg) del cfg del mcw - (singlepaths, mpaths, partitions) = identifyMultipaths(disks) + topology = DeviceTopology(disks) # The device list could be really long, so we really only want to # iterate over it the bare minimum of times. Dividing this list up # now means fewer elements to iterate over later. - singlepaths = filter(lambda info: self._device_size_is_nonzero(info), singlepaths) + singlepaths = filter(lambda info: self._device_size_is_nonzero(info), + topology.singlepaths_iter()) (raids, nonraids) = self.split_list(lambda d: isRAID(d) and not isCCISS(d), singlepaths) @@ -614,7 +616,7 @@ class FilterWindow(InstallWindow): self._makeMPath(), self._makeOther(), self._makeSearch()] - self.populate(nonraids, mpaths, raids) + self.populate(nonraids, topology.multipaths_iter(), raids) # If the "Add Advanced" button is ever clicked, we need to have a list # of what devices previously existed so we know what's new. Then we @@ -627,8 +629,8 @@ class FilterWindow(InstallWindow): # lists, we can't directly store that into the cache. Instead we want # to flatten it into a single list of all components of all multipaths # and store that. - lst = list(itertools.chain(*mpaths)) - self._cachedMPaths = NameCache(lst) + mpath_chain = itertools.chain(*topology.multipaths_iter()) + self._cachedMPaths = NameCache(mpath_chain) # Switch to the first notebook page that displays any devices. i = 0 diff --git a/pyanaconda/storage/devicelibs/mpath.py b/pyanaconda/storage/devicelibs/mpath.py index 14d2bda..c73b172 100644 --- a/pyanaconda/storage/devicelibs/mpath.py +++ b/pyanaconda/storage/devicelibs/mpath.py @@ -3,6 +3,7 @@ import re from ..udev import * from pyanaconda import iutil +from pyanaconda.anaconda_log import log_method_call import logging log = logging.getLogger("storage") @@ -225,6 +226,106 @@ def identifyMultipaths(devices): log.info("devices post multipath scan: %s" % s) return (singlepath_disks, multipaths, partition_devices) + +class DeviceTopology(object): + def __init__(self, devices_list): + self._devices = devices_list + self._nondisks = [] + self._singlepaths = [] + self._multipaths = [] # mpath members + self._devmap = {} + + self._build_topology() + + def _build_devmap(self): + self._devmap = {} + for dev in self._devices: + self._devmap[dev['name']] = dev + + def _build_mpath_topology(self): + self._mpath_topology = parseMultipathOutput( + iutil.execWithCapture("multipath", ["-d",])) + self._mpath_topology.update(parseMultipathOutput( + iutil.execWithCapture("multipath", ["-ll",]))) + + delete_keys = [] + for (mp, disks) in self._mpath_topology.items(): + # single device mpath is not really an mpath, eliminate them: + if len(disks) < 2: + log.info("DeviceTopology: not a multipath: %s" % disks) + delete_keys.append(mp) + continue + # some usb cardreaders use multiple lun's (for different slots) and + # report a fake disk serial which is the same for all the lun's + # (#517603). find those mpaths and eliminate them: + only_non_usbs = [d for d in disks if + self._devmap[d].get("ID_USB_DRIVER") != "usb-storage"] + if len(only_non_usbs) == 0: + log.info("DeviceToppology: found multi lun usb " + "mass storage device: %s" % disks) + delete_keys.append(mp) + map(lambda key: self._mpath_topology.pop(key), delete_keys) + + def _build_topology(self): + log_method_call(self) + self._build_devmap() + self._build_mpath_topology() + + for dev in self._devices: + name = dev['name'] + if not udev_device_is_disk(dev): + self._nondisks.append(name) + log.info("DeviceTopology: found non-disk device: %s" % name) + continue + mpath_name = self.multipath_name(name) + if mpath_name: + dev["ID_FS_TYPE"] = "multipath_member" + dev["ID_MPATH_NAME"] = mpath_name + log.info("DeviceTopology: found a multipath member of %s: %s " % + (mpath_name, name)) + continue + # it's a disk and not a multipath member (can be a coalesced + # multipath) + self._singlepaths.append(name) + log.info("DeviceTopology: found singlepath device: %s" % name) + + def devices_iter(self): + """ Generator. Yields all disk devices, mpaths members, coalesced mpath + devices and partitions. + + This property guarantees the order of the returned devices is the + same as in the device list passed to the object's constructor. + """ + for device in self._devices: + yield device + + def singlepaths_iter(self): + """ Generator. Yields only the singlepath disks. + """ + for name in self._singlepaths: + yield self._devmap[name] + + def multipath_name(self, mpath_member_name): + """ If the mpath_member_name is a member of a multipath device return + the name of the device (e.g. mpathc). + + Else return None. + """ + for (name, members) in self._mpath_topology.items(): + if mpath_member_name in members: + return name + return None + + def multipaths_iter(self): + """Generator. Yields all the multipath members, in a topology. + + Every iteration returns a list of mpath member devices forming a + multipath. + """ + for disks in self._mpath_topology.values(): + yield [self._devmap[d] for d in disks] + + class MultipathConfigWriter: def __init__(self): self.blacklist_devices = [] @@ -303,3 +404,4 @@ blacklist { ret += '}\n' return ret + diff --git a/pyanaconda/storage/devicetree.py b/pyanaconda/storage/devicetree.py index 829159c..f683ef1 100644 --- a/pyanaconda/storage/devicetree.py +++ b/pyanaconda/storage/devicetree.py @@ -1712,16 +1712,12 @@ class DeviceTree(object): open("/etc/multipath.conf", "w+").write(cfg) del cfg - devices = udev_get_block_devices() - (singles, mpaths, partitions) = devicelibs.mpath.identifyMultipaths(devices) - devices = singles + reduce(list.__add__, mpaths, []) + partitions - # remember all the devices idenitfyMultipaths() gave us at this point + self.topology = devicelibs.mpath.DeviceTopology(udev_get_block_devices()) + log.info("devices to scan: %s" % + [d['name'] for d in self.topology.devices_iter()]) old_devices = {} - for dev in devices: + for dev in self.topology.devices_iter(): old_devices[dev['name']] = dev - - log.info("devices to scan: %s" % [d['name'] for d in devices]) - for dev in devices: self.addUdevDevice(dev) # Having found all the disks, we can now find all the multipaths built -- 1.7.3.3 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list