aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'storage/devicelibs/mpath.py')
-rw-r--r--storage/devicelibs/mpath.py228
1 files changed, 228 insertions, 0 deletions
diff --git a/storage/devicelibs/mpath.py b/storage/devicelibs/mpath.py
new file mode 100644
index 0000000..adb2644
--- /dev/null
+++ b/storage/devicelibs/mpath.py
@@ -0,0 +1,228 @@
+from ..udev import *
+import iutil
+
+def parseMultipathOutput(output):
+ # this function parses output from "multipath -d", so we can use its
+ # logic for our topology.
+ # The input looks like:
+ # create: mpathb (1ATA ST3120026AS 5M) undef ATA,ST3120026AS
+ # size=112G features='0' hwhandler='0' wp=undef
+ # `-+- policy='round-robin 0' prio=1 status=undef
+ # `- 2:0:0:0 sda 8:0 undef ready running
+ # create: mpatha (36006016092d21800703762872c60db11) undef DGC,RAID 5
+ # size=10G features='1 queue_if_no_path' hwhandler='1 emc' wp=undef
+ # `-+- policy='round-robin 0' prio=2 status=undef
+ # |- 6:0:0:0 sdb 8:16 undef ready running
+ # `- 7:0:0:0 sdc 8:32 undef ready running
+ #
+ # (In anaconda, the first one there won't be included because we blacklist
+ # "ATA" as a vendor.)
+ #
+ # It returns a structure like:
+ # [ {'mpatha':['sdb','sdc']}, ... ]
+ mpaths = {}
+ if output is None:
+ return mpaths
+
+ name = None
+ devices = []
+
+ lines = output.split('\n')
+ for line in lines:
+ lexemes = line.split()
+ if not lexemes:
+ break
+ if lexemes[0] == 'create:':
+ if name and devices:
+ mpaths[name] = devices
+ name = None
+ devices = []
+ name = lexemes[1]
+ elif lexemes[0].startswith('size='):
+ pass
+ elif lexemes[0] == '`-+-':
+ pass
+ elif lexemes[0] in ['|-','`-']:
+ devices.append(lexemes[2].replace('!', '/'))
+
+ if name and devices:
+ mpaths[name] = devices
+
+ return mpaths
+
+def identifyMultipaths(devices):
+ # this function does a couple of things
+ # 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 pair [sdb,sdc]
+ # [sr0, sda, sda1, sdb, sdb1, sdb2, sdc, sdc1, sdd, sdd1, sdd2]
+ # sample output:
+ # [sda, sdd], [[sdb, sdc]], [sr0, sda1, sdd1, sdd2]]
+ log.info("devices to scan for multipath: %s" % [d['name'] for d in devices])
+
+ 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:
+ 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
+ devmap[d['name']] = d
+
+ singlepath_disks = []
+ multipaths = []
+
+ for name, disks in topology.items():
+ if len(disks) == 1:
+ 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
+ # 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
+ if (not devmap.has_key(disk)) and non_disk_devices.has_key(disk):
+ log.warning("non-disk device %s is part of an mpath" %
+ (disk,))
+ non_disks = True
+
+ if all_usb:
+ log.info("adding multi lun usb mass storage device to singlepath_disks: %s" %
+ (disks,))
+ singlepath_disks.extend([devmap[d] for d in disks])
+ continue
+
+ if non_disks:
+ for disk in disks:
+ if devmap.has_key(disk):
+ del devmap[disk]
+ if topology.has_key(disk):
+ del topology[disk]
+ continue
+
+ 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])
+
+ 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.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 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.
+ singlepath_disks = filter(lambda d: d in devices, singlepath_disks)
+ #multipaths = filter(lambda d: d in devices, multipaths)
+ partition_devices = filter(lambda d: d in devices, partition_devices)
+
+ mpathStr = "["
+ for mpath in multipaths:
+ mpathStr += str([d['name'] for d in mpath])
+ mpathStr += "]"
+
+ s = "(%s, %s, %s)" % ([d['name'] for d in singlepath_disks], \
+ mpathStr, \
+ [d['name'] for d in partition_devices])
+ log.info("devices post multipath scan: %s" % s)
+ return (singlepath_disks, multipaths, partition_devices)
+
+class MultipathConfigWriter:
+ def __init__(self):
+ self.blacklist_devices = []
+ self.mpaths = []
+
+ def addBlacklistDevice(self, device):
+ self.blacklist_devices.append(device)
+
+ def addMultipathDevice(self, mpath):
+ self.mpaths.append(mpath)
+
+ def write(self):
+ # if you add anything here, be sure and also add it to anaconda's
+ # multipath.conf
+ ret = ''
+ ret += """\
+# multipath.conf written by anaconda
+
+defaults {
+ user_friendly_names yes
+}
+blacklist {
+ devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
+ devnode "^hd[a-z]"
+ devnode "^dcssblk[0-9]*"
+ device {
+ vendor "DGC"
+ product "LUNZ"
+ }
+ device {
+ vendor "IBM"
+ product "S/390.*"
+ }
+ # don't count normal SATA devices as multipaths
+ device {
+ vendor "ATA"
+ }
+ # don't count 3ware devices as multipaths
+ device {
+ vendor "3ware"
+ }
+ device {
+ vendor "AMCC"
+ }
+ # nor highpoint devices
+ device {
+ vendor "HPT"
+ }
+"""
+ for device in self.blacklist_devices:
+ if device.serial:
+ ret += '\twwid %s\n' % device.serial
+ elif device.vendor and device.model:
+ ret += '\tdevice {\n'
+ ret += '\t\tvendor %s\n' % device.vendor
+ ret += '\t\tproduct %s\n' % device.model
+ ret += '\t}\n'
+ ret += '}\n'
+ ret += 'multipaths {\n'
+ for mpath in self.mpaths:
+ ret += '\tmultipath {\n'
+ for k,v in mpath.config.items():
+ ret += '\t\t%s %s\n' % (k, v)
+ ret += '\t}\n'
+ ret += '}\n'
+
+ return ret