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

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

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


Revision 1.2 - (show annotations) (download) (as text)
Sun Dec 22 04:27:52 2013 UTC (10 years, 10 months ago) by wellsi
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +0 -0 lines
Content type: text/x-python
FILE REMOVED
anaconda updates now handled via patches

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