/[smeserver]/cdrom.image/sme9/updates/storage/devicelibs/mpath.py
ViewVC logotype

Annotation of /cdrom.image/sme9/updates/storage/devicelibs/mpath.py

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (hide annotations) (download) (as text)
Sun Oct 20 23:12:54 2013 UTC (11 years ago) by charliebrady
Branch: MAIN
Content type: text/x-python
Add devicelibs and formats subdirectories of storage, and contained .py
files, in preparation to modifying to allow degraded RAID install.

1 charliebrady 1.1
2     import re
3    
4     from ..udev import *
5     from flags import flags
6     import iutil
7     import logging
8    
9     log = logging.getLogger("storage")
10    
11     def _filter_out_mpath_devices(devices):
12     retval = []
13     for d in devices:
14     if udev_device_is_dm_mpath(d):
15     log.debug("filtering out coalesced mpath device: %s" % d['name'])
16     else:
17     retval.append(d)
18     return retval
19    
20     def _filter_out_mpath_partitions(devices, multipaths):
21     """
22     Use serial numbers of the multipath members to filter devices from the
23     devices list. The returned list will only partitions that are NOT partitions
24     of the multipath members.
25     """
26     serials = set(udev_device_get_serial(d)
27     for mpath_members in multipaths for d in mpath_members)
28     retval = []
29     for d in devices:
30     if udev_device_get_serial(d) in serials:
31     log.debug("filtering out mpath partition: %s" % d['name'])
32     else:
33     retval.append(d)
34     return retval
35    
36     def parseMultipathOutput(output):
37     """
38     Parse output from "multipath -d" or "multipath -ll" and form a topology.
39    
40     Returns a dictionary:
41     {'mpatha':['sdb','sdc'], 'mpathb': ['sdd', 'sde'], ... }
42    
43     The 'multipath -d' output looks like:
44     create: mpathc (1ATA ST3120026AS 5M) undef ATA,ST3120026AS
45     size=112G features='0' hwhandler='0' wp=undef
46     `-+- policy='round-robin 0' prio=1 status=undef
47     `- 2:0:0:0 sda 8:0 undef ready running
48     create: mpathb (36006016092d21800703762872c60db11) undef DGC,RAID 5
49     size=10G features='1 queue_if_no_path' hwhandler='1 emc' wp=undef
50     `-+- policy='round-robin 0' prio=2 status=undef
51     |- 6:0:0:0 sdb 8:16 undef ready running
52     `- 7:0:0:0 sdc 8:32 undef ready running
53     create: mpatha (36001438005deb4710000500000270000) dm-0 HP,HSV400
54     size=20G features='0' hwhandler='0' wp=rw
55     |-+- policy='round-robin 0' prio=-1 status=active
56     | |- 7:0:0:1 sda 8:0 active undef running
57     | `- 7:0:1:1 sdb 8:16 active undef running
58     `-+- policy='round-robin 0' prio=-1 status=enabled
59     |- 7:0:2:1 sdc 8:32 active undef running
60     `- 7:0:3:1 sdd 8:48 active undef running
61    
62     (In anaconda, the first one there won't be included because we blacklist
63     "ATA" as a vendor.)
64    
65     The 'multipath -ll' output looks like (notice the missing 'create' before
66     'mpatha'):
67    
68     mpatha (3600a0b800067fcc9000001694b557dd1) dm-0 IBM,1726-4xx FAStT
69     size=360G features='0' hwhandler='1 rdac' wp=rw
70     `-+- policy='round-robin 0' prio=3 status=active
71     |- 2:0:0:0 sda 8:0 active ready running
72     `- 3:0:0:0 sdb 8:16 active ready running
73    
74     """
75     mpaths = {}
76     if output is None:
77     return mpaths
78    
79     action = None
80     name = None
81     devices = []
82    
83     policy = re.compile('^[|+` -]+policy')
84     device = re.compile('^[|+` -]+[0-9]+:[0-9]+:[0-9]+:[0-9]+ +([a-zA-Z0-9!/]+)')
85     create = re.compile('^(?:([a-z]+): )?(mpath\w+|[a-f0-9]+)')
86    
87     lines = output.split('\n')
88     for line in lines:
89     pmatch = policy.match(line)
90     dmatch = device.match(line)
91     cmatch = create.match(line)
92     lexemes = line.split()
93     if not lexemes:
94     break
95     if cmatch and cmatch.group(2):
96     if name and devices and action in (None, 'create'):
97     mpaths[name] = devices
98     action = cmatch.group(1)
99     name = cmatch.group(2)
100     devices = []
101     elif lexemes[0].startswith('size='):
102     pass
103     elif pmatch:
104     pass
105     elif dmatch:
106     devices.append(dmatch.groups()[0].replace('!','/'))
107    
108     if name and devices and action in (None, 'create'):
109     mpaths[name] = devices
110    
111     return mpaths
112    
113     def identifyMultipaths(devices):
114     """
115     This function does a couple of things:
116     1) identifies multipath disks,
117     2) sets their ID_FS_TYPE to multipath_member,
118     3) removes the individual members of an mpath's partitions as well as any
119     coalesced multipath devices:
120    
121     The return value is a tuple of 3 lists, the first list containing all
122     devices except multipath members and partitions, the second list containing
123     only the multipath members and the last list only partitions. Specifically,
124     the second list is empty if there are no multipath devices found.
125    
126     sample input:
127     [sr0, sda, sda1, sdb, sdb1, sdb2, sdc, sdc1, sdd, sdd1, sdd2, dm-0]
128     where:
129     [sdb, sdc] is a multipath pair
130     dm-0 is a mutliapth device already coalesced from [sdb, sdc]
131    
132     sample output:
133     [sda, sdd], [[sdb, sdc]], [sr0, sda1, sdd1, sdd2]]
134     """
135     log.info("devices to scan for multipath: %s" % [d['name'] for d in devices])
136    
137     with open("/etc/multipath.conf") as conf:
138     log.debug("/etc/multipath.conf contents:")
139     map(lambda line: log.debug(line.rstrip()), conf)
140     log.debug("(end of /etc/multipath.conf)")
141    
142     topology = parseMultipathOutput(
143     iutil.execWithCapture("multipath", ["-d",]))
144     topology.update(parseMultipathOutput(
145     iutil.execWithCapture("multipath", ["-ll",])))
146     # find the devices that aren't in topology, and add them into it...
147     topodevs = reduce(lambda x,y: x.union(y), topology.values(), set())
148     for name in set([d['name'] for d in devices]).difference(topodevs):
149     topology[name] = [name]
150    
151     devmap = {}
152     non_disk_devices = {}
153     for d in devices:
154     if not udev_device_is_disk(d):
155     non_disk_devices[d['name']] = d
156     log.info("adding %s to non_disk_device list" % (d['name'],))
157     continue
158     devmap[d['name']] = d
159    
160     singlepath_disks = []
161     multipaths = []
162    
163     for name, disks in topology.items():
164     if len(disks) == 1:
165     if not non_disk_devices.has_key(disks[0]):
166     log.info("adding %s to singlepath_disks" % (disks[0],))
167     singlepath_disks.append(devmap[disks[0]])
168     else:
169     # some usb cardreaders use multiple lun's (for different slots)
170     # and report a fake disk serial which is the same for all the
171     # lun's (#517603)
172     all_usb = True
173     # see if we've got any non-disk devices on our mpath list.
174     # If so, they're probably false-positives.
175     non_disks = False
176     for disk in disks:
177     d = devmap[disk]
178     if d.get("ID_USB_DRIVER") != "usb-storage":
179     all_usb = False
180     if (not devmap.has_key(disk)) and non_disk_devices.has_key(disk):
181     log.warning("non-disk device %s is part of an mpath" %
182     (disk,))
183     non_disks = True
184    
185     if all_usb:
186     log.info("adding multi lun usb mass storage device to singlepath_disks: %s" %
187     (disks,))
188     singlepath_disks.extend([devmap[d] for d in disks])
189     continue
190    
191     if non_disks:
192     for disk in disks:
193     if devmap.has_key(disk):
194     del devmap[disk]
195     if topology.has_key(disk):
196     del topology[disk]
197     continue
198    
199     log.info("found multipath set: %s" % (disks,))
200     for disk in disks:
201     d = devmap[disk]
202     log.info("adding %s to multipath_disks" % (disk,))
203     d["ID_FS_TYPE"] = "multipath_member"
204     d["ID_MPATH_NAME"] = name
205    
206     multipaths.append([devmap[d] for d in disks])
207    
208     # singlepaths and partitions should not contain multipath devices:
209     singlepath_disks = _filter_out_mpath_devices(singlepath_disks)
210     partition_devices = _filter_out_mpath_partitions(
211     non_disk_devices.values(), multipaths)
212    
213     # this is the list of devices we want to keep from the original
214     # device list, but we want to maintain its original order.
215     singlepath_disks = filter(lambda d: d in devices, singlepath_disks)
216     #multipaths = filter(lambda d: d in devices, multipaths)
217     partition_devices = filter(lambda d: d in devices, partition_devices)
218    
219     mpathStr = "["
220     for mpath in multipaths:
221     mpathStr += str([d['name'] for d in mpath])
222     mpathStr += "]"
223    
224     s = "(%s, %s, %s)" % ([d['name'] for d in singlepath_disks], \
225     mpathStr, \
226     [d['name'] for d in partition_devices])
227     log.info("devices post multipath scan: %s" % s)
228     return (singlepath_disks, multipaths, partition_devices)
229    
230     class MultipathConfigWriter:
231     def __init__(self):
232     self.blacklist_devices = []
233     self.mpaths = []
234    
235     def addBlacklistDevice(self, device):
236     self.blacklist_devices.append(device)
237    
238     def addMultipathDevice(self, mpath):
239     self.mpaths.append(mpath)
240    
241     def write(self, friendly_names):
242     # if you add anything here, be sure and also add it to anaconda's
243     # multipath.conf
244     ret = ''
245     ret += """\
246     # multipath.conf written by anaconda
247    
248     defaults {
249     user_friendly_names %(friendly_names)s
250     }
251     blacklist {
252     devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
253     devnode "^hd[a-z]"
254     devnode "^dcssblk[0-9]*"
255     device {
256     vendor "DGC"
257     product "LUNZ"
258     }
259     device {
260     vendor "IBM"
261     product "S/390.*"
262     }
263     # don't count normal SATA devices as multipaths
264     device {
265     vendor "ATA"
266     }
267     # don't count 3ware devices as multipaths
268     device {
269     vendor "3ware"
270     }
271     device {
272     vendor "AMCC"
273     }
274     # nor highpoint devices
275     device {
276     vendor "HPT"
277     }
278     """ % {'friendly_names' : "yes" if friendly_names else "no"}
279     for device in self.blacklist_devices:
280     if device.serial:
281     ret += '\twwid "%s"\n' % device.serial
282     elif device.vendor and device.model:
283     ret += '\tdevice {\n'
284     ret += '\t\tvendor %s\n' % device.vendor
285     ret += '\t\tproduct %s\n' % device.model
286     ret += '\t}\n'
287     if self.mpaths:
288     ret += '\twwid "*"\n'
289     ret += '}\n'
290     ret += 'blacklist_exceptions {\n'
291     for mpath in self.mpaths:
292     for k,v in mpath.config.items():
293     if k == 'wwid':
294     ret += '\twwid "%s"\n' % v
295     ret += '}\n'
296     ret += 'multipaths {\n'
297     for mpath in self.mpaths:
298     ret += '\tmultipath {\n'
299     for k,v in mpath.config.items():
300     if k == 'wwid':
301     ret += '\t\twwid "%s"\n' % v
302     else:
303     ret += '\t\t%s %s\n' % (k, v)
304     ret += '\t}\n'
305     ret += '}\n'
306    
307     return ret
308    
309     def writeMultipathConf(writer=None, friendly_names=True):
310     if not flags.mpath:
311     # not writing out a multipath.conf will effectively blacklist all mpaths
312     # which will prevent any of them from being activated during install
313     return
314    
315     if writer is None:
316     writer = MultipathConfigWriter()
317    
318     cfg = writer.write(friendly_names=friendly_names)
319     with open("/etc/multipath.conf", "w+") as mpath_cfg:
320     mpath_cfg.write(cfg)
321    
322     def flush_mpaths():
323     iutil.execWithRedirect("multipath", ["-F"])
324     check_output = iutil.execWithCapture("multipath", ["-ll"]).strip()
325     if check_output:
326     log.error("multipath: some devices could not be flushed")

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed