Use device-mapper-multipath's "multipath" tool to find and set up multipath devices. --- storage/devicelibs/mpath.py | 91 +++++++++++++++++++++++++++++-------------- storage/devices.py | 78 ++++++++++++++++++++++++++++++------- 2 files changed, 125 insertions(+), 44 deletions(-) diff --git a/storage/devicelibs/mpath.py b/storage/devicelibs/mpath.py index 4d1b262..0f3efc2 100644 --- a/storage/devicelibs/mpath.py +++ b/storage/devicelibs/mpath.py @@ -1,10 +1,13 @@ from ..udev import * +import iutil def parseMultipathOutput(output): # this function parses output from "multipath -d", so we can use its # logic for our topology. It returns a structure like: # [ {'mpathb':['sdb','sdc']}, ... ] mpaths = {} + if output is None: + return mpaths name = None devices = [] @@ -37,63 +40,91 @@ def identifyMultipaths(devices): # 1) identifies multipath disks # 2) sets their ID_FS_TYPE to multipath_member # 3) removes the individual members of an mpath's partitions - # sample input with multipath pairs [sda,sdc] and [sdb,sdd] - # [sr0, sda, sda1, sdb, sda2, sdc, sdd, sdc1, sdc2, sde, sde1] + # sample input with multipath pair [sdb,sdc] + # [sr0, sda, sda1, sdb, sdb1, sdb2, sdc, sdc1, sdd, sdd1, sdd2] # sample output: - # [sr0, sda, sdb, sdc, sdd, sde, sde1] - + # [sda, sdd], [[sdb, sdc]], [sr0, sda1, sdd1, sdd2]] log.info("devices to scan for multipath: %s" % [d['name'] for d in devices]) - serials = {} + + topology = parseMultipathOutput(iutil.execWithCapture("multipath", ["-d",])) + # find the devices that aren't in topology, and add them into it... + topodevs = reduce(lambda x,y: x.union(y), topology.values(), set()) + for name in set([d['name'] for d in devices]).difference(topodevs): + topology[name] = [name] + + devmap = {} non_disk_devices = {} for d in devices: - serial = udev_device_get_serial(d) - if (not udev_device_is_disk(d)) or \ - (not d.has_key('ID_SERIAL_SHORT')): - non_disk_devices.setdefault(serial, []) - non_disk_devices[serial].append(d) + if not udev_device_is_disk(d): + non_disk_devices[d['name']] = d log.info("adding %s to non_disk_device list" % (d['name'],)) continue - - serials.setdefault(serial, []) - serials[serial].append(d) + devmap[d['name']] = d singlepath_disks = [] multipaths = [] - for serial, disks in serials.items(): + + for name, disks in topology.items(): if len(disks) == 1: - log.info("adding %s to singlepath_disks" % (disks[0]['name'],)) - singlepath_disks.append(disks[0]) + if not non_disk_devices.has_key(disks[0]): + log.info("adding %s to singlepath_disks" % (disks[0],)) + singlepath_disks.append(devmap[disks[0]]) else: # 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) all_usb = True - for d in disks: + # see if we've got any non-disk devices on our mpath list. + # If so, they're probably false-positives. + non_disks = False + for disk in disks: + d = devmap[disk] if d.get("ID_USB_DRIVER") != "usb-storage": all_usb = False - break + if (not devmap.has_key(disk)) and non_disk_devices.has_key(disk): + non_disks = True + if all_usb: log.info("adding multi lun usb mass storage device to singlepath_disks: %s" % - [disk['name'] for disk in disks]) - singlepath_disks.extend(disks) + (disks,)) + singlepath_disks.extend([devmap[d] for d in disks]) + continue + + if non_disks: + log.warning("non-disk device %s is part of an mpath") + for disk in disks: + if devmap.has_key(disk): + del devmap[disk] + if topology.has_key(disk): + del topology[disk] continue - for d in disks: - log.info("adding %s to multipath_disks" % (d['name'],)) + log.info("found multipath set: %s" % (disks,)) + for disk in disks: + d = devmap[disk] + log.info("adding %s to multipath_disks" % (disk,)) d["ID_FS_TYPE"] = "multipath_member" + d["ID_MPATH_NAME"] = name + + multipaths.append([devmap[d] for d in disks]) - multipaths.append(disks) - log.info("found multipath set: [%s]" % [d['name'] for d in disks]) + non_disk_serials = {} + for name,device in non_disk_devices.items(): + serial = udev_device_get_serial(device) + non_disk_serials.setdefault(serial, []) + non_disk_serials[serial].append(device) for mpath in multipaths: - for serial in [d['ID_SERIAL_SHORT'] for d in mpath]: - if non_disk_devices.has_key(serial): - log.info("filtering out non disk devices [%s]" % [d['name'] for d in non_disk_devices[serial]]) - del non_disk_devices[serial] + for serial in [d.get('ID_SERIAL_SHORT') for d in mpath]: + if non_disk_serials.has_key(serial): + log.info("filtering out non disk devices [%s]" % [d['name'] for d in non_disk_serials[serial]]) + for name in [d['name'] for d in non_disk_serials[serial]]: + if non_disk_devices.has_key(name): + del non_disk_devices[name] partition_devices = [] - for devs in non_disk_devices.values(): - partition_devices += devs + for device in non_disk_devices.values(): + partition_devices.append(device) # this is the list of devices we want to keep from the original # device list, but we want to maintain its original order. diff --git a/storage/devices.py b/storage/devices.py index 131db2a..0da4aed 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2931,11 +2931,8 @@ class MultipathDevice(DMDevice): """ self._info = info - self._isUp = False - self._pyBlockMultiPath = None self.setupIdentity() DMDevice.__init__(self, name, format=format, size=size, - parents=parents, sysfsPath=sysfsPath) parents=parents, sysfsPath=sysfsPath, exists=True) @@ -3008,21 +3005,74 @@ class MultipathDevice(DMDevice): else: self.parents.append(parent) - def setup(self, intf=None): - if self.status: - self.teardown() - self._isUp = True - parents = [] - for p in self.parents: - parents.append(p.path) - self._pyBlockMultiPath = block.device.MultiPath(*parents) + def teardownPartitions(self): + log_method_call(self, name=self.name, kids=self.kids) + rc = iutil.execWithRedirect("kpartx", + ["-d", "-p", "p", "/dev/mapper/%s" % self.name], + stdout = "/dev/tty5", + stderr = "/dev/tty5") + if rc: + raise MPathError("multipath partition deactivation failed for '%s'"\ + % self.name) + udev_settle() + + def setupPartitions(self): + log_method_call(self, name=self.name, kids=self.kids) + rc = iutil.execWithRedirect("kpartx", + ["-a", "-p", "p", "/dev/mapper/%s" % self.name], + stdout = "/dev/tty5", + stderr = "/dev/tty5") + if rc: + raise MPathError("multipath partition activation failed for '%s'" % + self.name) + udev_settle() + + def makeDMNodes(self): + log_method_call(self, name=self.name, kids=self.kids) + rc = iutil.execWithRedirect("dmsetup", ["mknodes"], + stdout = "/dev/tty5", + stderr = "/dev/tty5") def teardown(self, recursive=None): - if not self.status: + """ Tear down the mpath device. """ + log_method_call(self, self.name, status=self.status) + + if not self.exists and not recursive: + raise DeviceError("device has not been created", self.name) + + if self.exists and os.path.exists(self.path): + self.teardownPartitions() + rc = iutil.execWithRedirect("multipath", + ['-f', self.name], + stdout = "/dev/tty5", + stderr = "/dev/tty5") + if rc: + raise MPathError("multipath deactivation failed for '%s'" % + self.name) + udev_settle() + + if recursive: + self.teardownParents(recursive=recursive) + + def setup(self, intf=None): + """ Open, or set up, a device. """ + log_method_call(self, self.name, status=self.status) + + if self.status: return - self._isUp = False - self._pyBlockMultiPath = None + StorageDevice.setup(self, intf=intf) + udev_settle() + rc = iutil.execWithRedirect("multipath", + [self.name], + stdout = "/dev/tty5", + stderr = "/dev/tty5") + if rc: + raise MPathError("multipath activation failed for '%s'" % + self.name) + udev_settle() + self.setupPartitions() + udev_settle() class NoDevice(StorageDevice): """ A nodev device for nodev filesystems like tmpfs. """ -- 1.6.5.2 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list