Coverage for drivers/devscan.py : 50%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (C) Citrix Systems Inc.
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU Lesser General Public License as published
5# by the Free Software Foundation; version 2.1 only.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU Lesser General Public License for more details.
11#
12# You should have received a copy of the GNU Lesser General Public License
13# along with this program; if not, write to the Free Software Foundation, Inc.,
14# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16import os
17import re
18import scsiutil
19import util
20import xml.dom.minidom
21import xs_errors
22import glob
23import fcoelib
25DEVPATH = '/dev/disk/by-id'
26DMDEVPATH = '/dev/mapper'
27SYSFS_PATH1 = '/sys/class/scsi_host'
28SYSFS_PATH2 = '/sys/class/scsi_disk'
29SYSFS_PATH3 = '/sys/class/fc_transport'
31DRIVER_BLACKLIST = ['^(s|p|)ata_.*', '^ahci$', '^pdc_adma$', '^iscsi_tcp$', '^usb-storage$']
33INVALID_DEVICE_NAME = ''
36def getManufacturer(s):
37 (rc, stdout, stderr) = util.doexec(['/sbin/modinfo', '-d', s])
38 if stdout: 38 ↛ 41line 38 didn't jump to line 41, because the condition on line 38 was never false
39 return stdout.strip()
40 else:
41 return "Unknown"
44def update_devs_dict(devs, dev, entry):
45 if dev != INVALID_DEVICE_NAME:
46 devs[dev] = entry
49def adapters(filterstr="any"):
50 dict = {}
51 devs = {}
52 adt = {}
53 fcoe_eth_info = {}
54 fcoe_port_info = []
56 fcoe_port_info = fcoelib.parse_fcoe_port_name_info()
57 if filterstr == "fcoe": 57 ↛ 58line 57 didn't jump to line 58, because the condition on line 57 was never true
58 fcoe_eth_info = fcoelib.parse_fcoe_eth_info()
60 for a in os.listdir(SYSFS_PATH1):
61 proc = match_hbadevs(a, filterstr)
62 if not proc: 62 ↛ 63line 62 didn't jump to line 63, because the condition on line 62 was never true
63 continue
65 #Special casing for fcoe
66 port_name_path = os.path.join(SYSFS_PATH1, a, 'device', \
67 'fc_host', a, 'port_name')
68 port_name_path_exists = os.path.exists(port_name_path)
69 util.SMlog("Port name path exists %d" % port_name_path_exists)
70 if filterstr == "fcoe" and not port_name_path_exists: 70 ↛ 71line 70 didn't jump to line 71, because the condition on line 70 was never true
71 continue
72 if port_name_path_exists: 72 ↛ 73line 72 didn't jump to line 73, because the condition on line 72 was never true
73 port_name = _get_port_name(port_name_path)
74 #If we are probing for fcoe luns/ adapters and if the port name
75 #in /sys/class/scsi_host/a/device/fc_host/a/port_name does not match
76 #one in the output of 'fcoeadm -i', then we shouldn't display that
77 #lun/adapter.
78 #On the other hand, if we are probing for hba luns, and if the
79 #port name in /sys/class/scsi_host/a/device/fc_host/a/port_name
80 #matches one in the output of 'fcoeadm -i', then we shouldn't
81 #display that lun/adapter, because that would have been discovered
82 #via the FCoE protocol.
83 if (filterstr == "fcoe" and port_name not in fcoe_port_info) or \
84 (filterstr != "fcoe" and port_name in fcoe_port_info):
85 continue
87 adt[a] = proc
88 id = a.replace("host", "")
89 scsiutil.rescan([id])
90 emulex = False
91 paths = []
92 if proc == "lpfc":
93 emulex = True
94 paths.append(SYSFS_PATH3)
95 else:
96 for p in [os.path.join(SYSFS_PATH1, a, "device", "session*"), os.path.join(SYSFS_PATH1, a, "device"), \
97 os.path.join(SYSFS_PATH2, "%s:*" % id)]:
98 paths += glob.glob(p)
100 if not len(paths): 100 ↛ 101line 100 didn't jump to line 101, because the condition on line 100 was never true
101 continue
102 for path in paths:
103 for i in filter(match_targets, os.listdir(path)):
104 tgt = i.replace('target', '')
105 if emulex: 105 ↛ 108line 105 didn't jump to line 108, because the condition on line 105 was never false
106 sysfs = os.path.join(SYSFS_PATH3, i, "device")
107 else:
108 sysfs = SYSFS_PATH2
109 for lun in os.listdir(sysfs):
110 if not match_LUNs(lun, tgt): 110 ↛ 111line 110 didn't jump to line 111, because the condition on line 110 was never true
111 continue
112 if emulex: 112 ↛ 115line 112 didn't jump to line 115, because the condition on line 112 was never false
113 dir = os.path.join(sysfs, lun)
114 else:
115 dir = os.path.join(sysfs, lun, "device")
116 (dev, entry) = _extract_dev(dir, proc, id, lun)
117 update_devs_dict(devs, dev, entry)
118 # for new qlogic sysfs layout (rport under device, then target)
119 for i in filter(match_rport, os.listdir(path)): 119 ↛ 120line 119 didn't jump to line 120, because the loop on line 119 never started
120 newpath = os.path.join(path, i)
121 for j in filter(match_targets, os.listdir(newpath)):
122 tgt = j.replace('target', '')
123 sysfs = SYSFS_PATH2
124 for lun in os.listdir(sysfs):
125 if not match_LUNs(lun, tgt):
126 continue
127 #Special casing for fcoe, populating eth information
128 eth = ""
129 if i in fcoe_eth_info.keys():
130 eth = fcoe_eth_info[i]
131 dir = os.path.join(sysfs, lun, "device")
132 (dev, entry) = _extract_dev(dir, proc, id, lun, eth)
133 update_devs_dict(devs, dev, entry)
135 # for new mptsas sysfs entries, check for phy* node
136 for i in filter(match_phy, os.listdir(path)): 136 ↛ 137line 136 didn't jump to line 137, because the loop on line 136 never started
137 (target, lunid) = i.replace('phy-', '').split(':')
138 tgt = "%s:0:0:%s" % (target, lunid)
139 sysfs = SYSFS_PATH2
140 for lun in os.listdir(sysfs):
141 if not match_LUNs(lun, tgt):
142 continue
143 dir = os.path.join(sysfs, lun, "device")
144 (dev, entry) = _extract_dev(dir, proc, id, lun)
145 update_devs_dict(devs, dev, entry)
146 if path.startswith(SYSFS_PATH2):
147 os.path.join(path, "device", "block:*")
148 dev = _extract_dev_name(os.path.join(path, 'device'))
149 if dev in devs: 149 ↛ 150line 149 didn't jump to line 150, because the condition on line 149 was never true
150 continue
151 hbtl = os.path.basename(path)
152 (h, b, t, l) = hbtl.split(':')
153 entry = {'procname': proc, 'host': id, 'target': l}
154 update_devs_dict(devs, dev, entry)
156 dict['devs'] = devs
157 dict['adt'] = adt
158 return dict
161def _get_port_name(port_name_path):
162 port_name = 0
163 try:
164 f = open(port_name_path, 'r')
165 try:
166 line = f.readline()[:-1]
167 if not line in ['<NULL>', '(NULL)', '']:
168 port_name = int(line, 16)
169 util.SMlog("Port Name in sysfs is %d" % port_name)
170 finally:
171 f.close()
172 except IOError:
173 pass
174 return port_name
177def _get_driver_name(scsihost):
178 driver_name = 'Unknown'
179 if os.path.exists(os.path.join(SYSFS_PATH1, scsihost, 'fnic_state')): 179 ↛ 180line 179 didn't jump to line 180, because the condition on line 179 was never true
180 driver_name = 'fnic'
181 if os.path.exists(os.path.join(SYSFS_PATH1, scsihost, 'lpfc_fcp_class')): 181 ↛ 182line 181 didn't jump to line 182, because the condition on line 181 was never true
182 driver_name = 'lpfc'
183 if os.path.exists(os.path.join(SYSFS_PATH1, scsihost, '84xx_fw_version')): 183 ↛ 184line 183 didn't jump to line 184, because the condition on line 183 was never true
184 driver_name = 'qla2xxx'
185 if 'Unknown' == driver_name: 185 ↛ 198line 185 didn't jump to line 198, because the condition on line 185 was never false
186 namepath = os.path.join(SYSFS_PATH1, scsihost, 'driver_name')
187 if not os.path.exists(namepath): 187 ↛ 189line 187 didn't jump to line 189, because the condition on line 187 was never false
188 namepath = os.path.join(SYSFS_PATH1, scsihost, 'proc_name')
189 if os.path.exists(namepath): 189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true
190 try:
191 f = open(namepath, 'r')
192 line = f.readline()[:-1]
193 f.close()
194 if not line in ['<NULL>', '(NULL)', '']:
195 driver_name = line
196 except IOError:
197 pass
198 if 'Unknown' == driver_name: 198 ↛ 209line 198 didn't jump to line 209, because the condition on line 198 was never false
199 ueventpath = os.path.join(SYSFS_PATH1, scsihost, 'uevent')
200 if os.path.exists(ueventpath): 200 ↛ 201line 200 didn't jump to line 201, because the condition on line 200 was never true
201 try:
202 f = open(ueventpath, 'r')
203 for line in f:
204 if line.startswith('PHYSDEVDRIVER='):
205 driver_name = line.replace('PHYSDEVDRIVER=', '').strip()
206 f.close()
207 except IOError:
208 pass
209 return driver_name
212def _parseHostId(str):
213 id = str.split()
214 val = "%s:%s:%s" % (id[1], id[3], id[5])
215 return val.replace(',', '')
218def _genMPPHBA(id):
219 devs = scsiutil.cacheSCSIidentifiers()
220 mppdict = {}
221 for dev in devs:
222 item = devs[dev]
223 if item[1] == id:
224 arr = scsiutil._genArrayIdentifier(dev)
225 if not len(arr):
226 continue
227 try:
228 cmd = ['/usr/sbin/mppUtil', '-a']
229 for line in util.doexec(cmd)[1].split('\n'):
230 if line.find(arr) != -1:
231 rec = line.split()[0]
232 cmd2 = ['/usr/sbin/mppUtil', '-g', rec]
233 li = []
234 for newline in util.doexec(cmd2)[1].split('\n'):
235 if newline.find('hostId') != -1:
236 li.append(_parseHostId(newline))
237 mppdict[dev.split('/')[-1]] = li
238 except:
239 continue
240 return mppdict
243def match_hbadevs(s, filterstr):
244 driver_name = _get_driver_name(s)
245 if match_host(s) and not match_blacklist(driver_name) \ 245 ↛ 250line 245 didn't jump to line 250, because the condition on line 245 was never false
246 and (filterstr == "any" or filterstr == "fcoe" or \
247 match_filterstr(filterstr, driver_name)):
248 return driver_name
249 else:
250 return ""
253def match_blacklist(driver_name):
254 return re.search("(" + ")|(".join(DRIVER_BLACKLIST) + ")", driver_name)
257def match_filterstr(filterstr, driver_name):
258 return re.search("^%s" % filterstr, driver_name)
261def match_host(s):
262 return re.search("^host[0-9]", s)
265def match_rport(s):
266 regex = re.compile("^rport-*")
267 return regex.search(s, 0)
270def match_targets(s):
271 regex = re.compile("^target[0-9]")
272 return regex.search(s, 0)
275def match_phy(s):
276 regex = re.compile("^phy-*")
277 return regex.search(s, 0)
280def match_LUNs(s, prefix):
281 regex = re.compile("^%s" % prefix)
282 return regex.search(s, 0)
285def match_dev(s):
286 regex = re.compile("^block:")
287 return regex.search(s, 0)
290def _extract_dev_name(device_dir):
291 """Returns the name of the block device from sysfs e.g. 'sda'"""
292 return _get_block_device_name_with_kernel_3x(device_dir)
295def _get_block_device_name_with_kernel_3x(device_dir):
296 devs = glob.glob(os.path.join(device_dir, 'block/*'))
297 if len(devs):
298 # prune path to extract the device name
299 return os.path.basename(devs[0])
300 else:
301 return INVALID_DEVICE_NAME
304def _extract_dev(device_dir, procname, host, target, eths=""):
305 """Returns device name and creates dictionary entry for it"""
306 dev = _extract_dev_name(device_dir)
307 entry = {}
308 entry['procname'] = procname
309 entry['host'] = host
310 entry['target'] = target
311 entry['eth'] = eths
312 return (dev, entry)
315def _add_host_parameters_to_adapter(dom, adapter, host_class, host_id,
316 parameters):
317 """Adds additional information about the adapter to the the adapter node"""
318 host_path = os.path.join('/sys/class/', host_class, 'host%s' % (host_id))
319 if os.path.exists(host_path):
320 host_entry = dom.createElement(host_class)
321 adapter.appendChild(host_entry)
322 for parameter in parameters:
323 try:
324 filehandle = open(os.path.join(host_path, parameter))
325 parameter_value = filehandle.read(512).strip()
326 filehandle.close()
327 if parameter_value: 327 ↛ 322line 327 didn't jump to line 322, because the condition on line 327 was never false
328 entry = dom.createElement(parameter)
329 host_entry.appendChild(entry)
330 text_node = dom.createTextNode(parameter_value)
331 entry.appendChild(text_node)
332 except IOError:
333 pass
336def scan(srobj):
337 systemrootID = util.getrootdevID()
338 hbadict = srobj.hbadict
339 hbas = srobj.hbas
340 dom = xml.dom.minidom.Document()
341 e = dom.createElement("Devlist")
342 dom.appendChild(e)
344 if not os.path.exists(DEVPATH):
345 return dom.toprettyxml()
347 devs = srobj.devs
348 vdis = {}
350 for key in hbadict:
351 hba = hbadict[key]
352 path = os.path.join("/dev", key)
353 realpath = path
355 obj = srobj.vdi("")
356 try:
357 obj._query(realpath, devs[realpath][4])
358 except:
359 continue
361 # Test for root dev or existing PBD
362 if len(obj.SCSIid) and len(systemrootID) and util.match_scsiID(obj.SCSIid, systemrootID):
363 util.SMlog("Ignoring root device %s" % realpath)
364 continue
365 elif util.test_SCSIid(srobj.session, None, obj.SCSIid):
366 util.SMlog("SCSIid in use, ignoring (%s)" % obj.SCSIid)
367 continue
368 elif realpath not in devs:
369 continue
371 ids = devs[realpath]
372 obj.adapter = ids[1]
373 obj.channel = ids[2]
374 obj.id = ids[3]
375 obj.lun = ids[4]
376 obj.hba = hba['procname']
377 if 'eth' in hba and hba['eth']:
378 obj.eth = hba['eth']
379 obj.numpaths = 1
380 if obj.SCSIid in vdis:
381 vdis[obj.SCSIid].numpaths += 1
382 vdis[obj.SCSIid].path += " [%s]" % key
383 elif obj.hba == 'mpp':
384 mppdict = _genMPPHBA(obj.adapter)
385 if key in mppdict:
386 item = mppdict[key]
387 adapters = ''
388 for i in item:
389 if len(adapters):
390 adapters += ', '
391 obj.numpaths += 1
392 adapters += i
393 if len(adapters):
394 obj.mpp = adapters
395 vdis[obj.SCSIid] = obj
396 else:
397 vdis[obj.SCSIid] = obj
399 for key in vdis: 399 ↛ 400line 399 didn't jump to line 400, because the loop on line 399 never started
400 obj = vdis[key]
401 d = dom.createElement("BlockDevice")
402 e.appendChild(d)
404 for attr in ['path', 'numpaths', 'SCSIid', 'vendor', 'serial', 'size', 'adapter', 'channel', 'id', 'lun', 'hba', 'mpp', 'eth']:
405 try:
406 aval = getattr(obj, attr)
407 except AttributeError:
408 if attr in ['mpp'] or attr in ['eth']:
409 continue
410 raise xs_errors.XenError('InvalidArg', \
411 opterr='Missing required field [%s]' % attr)
412 entry = dom.createElement(attr)
413 d.appendChild(entry)
414 textnode = dom.createTextNode(str(aval))
415 entry.appendChild(textnode)
417 for key in hbas:
418 a = dom.createElement("Adapter")
419 e.appendChild(a)
420 entry = dom.createElement('host')
421 a.appendChild(entry)
422 textnode = dom.createTextNode(key)
423 entry.appendChild(textnode)
425 entry = dom.createElement('name')
426 a.appendChild(entry)
427 textnode = dom.createTextNode(hbas[key])
428 entry.appendChild(textnode)
430 entry = dom.createElement('manufacturer')
431 a.appendChild(entry)
432 textnode = dom.createTextNode(getManufacturer(hbas[key]))
433 entry.appendChild(textnode)
435 id = key.replace("host", "")
436 entry = dom.createElement('id')
437 a.appendChild(entry)
438 textnode = dom.createTextNode(id)
439 entry.appendChild(textnode)
441 _add_host_parameters_to_adapter(dom, a, 'fc_host', id,
442 ['node_name', 'port_name',
443 'port_state', 'speed',
444 'supported_speeds'])
445 _add_host_parameters_to_adapter(dom, a, 'iscsi_host', id,
446 ['hwaddress', 'initiatorname',
447 'ipaddress', 'port_speed',
448 'port_state'])
450 return dom.toprettyxml()
453def check_iscsi(adapter):
454 ret = False
455 str = "host%s" % adapter
456 try:
457 filename = os.path.join('/sys/class/scsi_host', str, 'proc_name')
458 f = open(filename, 'r')
459 if f.readline().find("iscsi_tcp") != -1:
460 ret = True
461 except:
462 pass
463 return ret
466def match_nonpartitions(s):
467 regex = re.compile("-part[0-9]")
468 if not regex.search(s, 0):
469 return True