Coverage for drivers/devscan.py : 57%

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 %s" % 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 match_hbadevs(s, filterstr):
219 driver_name = _get_driver_name(s)
220 if match_host(s) and not match_blacklist(driver_name) \ 220 ↛ 225line 220 didn't jump to line 225, because the condition on line 220 was never false
221 and (filterstr == "any" or filterstr == "fcoe" or \
222 match_filterstr(filterstr, driver_name)):
223 return driver_name
224 else:
225 return ""
228def match_blacklist(driver_name):
229 return re.search("(" + ")|(".join(DRIVER_BLACKLIST) + ")", driver_name)
232def match_filterstr(filterstr, driver_name):
233 return re.search("^%s" % filterstr, driver_name)
236def match_host(s):
237 return re.search("^host[0-9]", s)
240def match_rport(s):
241 regex = re.compile("^rport-*")
242 return regex.search(s, 0)
245def match_targets(s):
246 regex = re.compile("^target[0-9]")
247 return regex.search(s, 0)
250def match_phy(s):
251 regex = re.compile("^phy-*")
252 return regex.search(s, 0)
255def match_LUNs(s, prefix):
256 regex = re.compile("^%s" % prefix)
257 return regex.search(s, 0)
260def match_dev(s):
261 regex = re.compile("^block:")
262 return regex.search(s, 0)
265def _extract_dev_name(device_dir):
266 """Returns the name of the block device from sysfs e.g. 'sda'"""
267 return _get_block_device_name_with_kernel_3x(device_dir)
270def _get_block_device_name_with_kernel_3x(device_dir):
271 devs = glob.glob(os.path.join(device_dir, 'block/*'))
272 if len(devs):
273 # prune path to extract the device name
274 return os.path.basename(devs[0])
275 else:
276 return INVALID_DEVICE_NAME
279def _extract_dev(device_dir, procname, host, target, eths=""):
280 """Returns device name and creates dictionary entry for it"""
281 dev = _extract_dev_name(device_dir)
282 entry = {}
283 entry['procname'] = procname
284 entry['host'] = host
285 entry['target'] = target
286 entry['eth'] = eths
287 return (dev, entry)
290def _add_host_parameters_to_adapter(dom, adapter, host_class, host_id,
291 parameters):
292 """Adds additional information about the adapter to the the adapter node"""
293 host_path = os.path.join('/sys/class/', host_class, 'host%s' % (host_id))
294 if os.path.exists(host_path):
295 host_entry = dom.createElement(host_class)
296 adapter.appendChild(host_entry)
297 for parameter in parameters:
298 try:
299 filehandle = open(os.path.join(host_path, parameter))
300 parameter_value = filehandle.read(512).strip()
301 filehandle.close()
302 if parameter_value: 302 ↛ 297line 302 didn't jump to line 297, because the condition on line 302 was never false
303 entry = dom.createElement(parameter)
304 host_entry.appendChild(entry)
305 text_node = dom.createTextNode(parameter_value)
306 entry.appendChild(text_node)
307 except IOError:
308 pass
311def scan(srobj):
312 systemrootID = util.getrootdevID()
313 hbadict = srobj.hbadict
314 hbas = srobj.hbas
315 dom = xml.dom.minidom.Document()
316 e = dom.createElement("Devlist")
317 dom.appendChild(e)
319 if not os.path.exists(DEVPATH):
320 return dom.toprettyxml()
322 devs = srobj.devs
323 vdis = {}
325 for key in hbadict:
326 hba = hbadict[key]
327 path = os.path.join("/dev", key)
328 realpath = path
330 obj = srobj.vdi("")
331 try:
332 obj._query(realpath, devs[realpath][4])
333 except:
334 continue
336 # Test for root dev or existing PBD
337 if len(obj.SCSIid) and len(systemrootID) and util.match_scsiID(obj.SCSIid, systemrootID):
338 util.SMlog("Ignoring root device %s" % realpath)
339 continue
340 elif util.test_SCSIid(srobj.session, None, obj.SCSIid):
341 util.SMlog("SCSIid in use, ignoring (%s)" % obj.SCSIid)
342 continue
343 elif realpath not in devs:
344 continue
346 ids = devs[realpath]
347 obj.adapter = ids[1]
348 obj.channel = ids[2]
349 obj.id = ids[3]
350 obj.lun = ids[4]
351 obj.hba = hba['procname']
352 if 'eth' in hba and hba['eth']:
353 obj.eth = hba['eth']
354 obj.numpaths = 1
355 if obj.SCSIid in vdis:
356 vdis[obj.SCSIid].numpaths += 1
357 vdis[obj.SCSIid].path += " [%s]" % key
358 else:
359 vdis[obj.SCSIid] = obj
361 for key in vdis: 361 ↛ 362line 361 didn't jump to line 362, because the loop on line 361 never started
362 obj = vdis[key]
363 d = dom.createElement("BlockDevice")
364 e.appendChild(d)
366 for attr in ['path', 'numpaths', 'SCSIid', 'vendor', 'serial', 'size', 'adapter', 'channel', 'id', 'lun', 'hba', 'eth']:
367 try:
368 aval = getattr(obj, attr)
369 except AttributeError:
370 if attr in ['eth']:
371 continue
372 raise xs_errors.XenError('InvalidArg',
373 opterr='Missing required field [%s]' % attr)
374 entry = dom.createElement(attr)
375 d.appendChild(entry)
376 textnode = dom.createTextNode(str(aval))
377 entry.appendChild(textnode)
379 for key in hbas:
380 a = dom.createElement("Adapter")
381 e.appendChild(a)
382 entry = dom.createElement('host')
383 a.appendChild(entry)
384 textnode = dom.createTextNode(key)
385 entry.appendChild(textnode)
387 entry = dom.createElement('name')
388 a.appendChild(entry)
389 textnode = dom.createTextNode(hbas[key])
390 entry.appendChild(textnode)
392 entry = dom.createElement('manufacturer')
393 a.appendChild(entry)
394 textnode = dom.createTextNode(getManufacturer(hbas[key]))
395 entry.appendChild(textnode)
397 id = key.replace("host", "")
398 entry = dom.createElement('id')
399 a.appendChild(entry)
400 textnode = dom.createTextNode(id)
401 entry.appendChild(textnode)
403 _add_host_parameters_to_adapter(dom, a, 'fc_host', id,
404 ['node_name', 'port_name',
405 'port_state', 'speed',
406 'supported_speeds'])
407 _add_host_parameters_to_adapter(dom, a, 'iscsi_host', id,
408 ['hwaddress', 'initiatorname',
409 'ipaddress', 'port_speed',
410 'port_state'])
412 return dom.toprettyxml()
415def check_iscsi(adapter):
416 ret = False
417 str = "host%s" % adapter
418 try:
419 filename = os.path.join('/sys/class/scsi_host', str, 'proc_name')
420 f = open(filename, 'r')
421 if f.readline().find("iscsi_tcp") != -1:
422 ret = True
423 except:
424 pass
425 return ret
428def match_nonpartitions(s):
429 regex = re.compile("-part[0-9]")
430 if not regex.search(s, 0):
431 return True