Coverage for drivers/devscan.py : 60%
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'
30PFX_PATH = '/dev/disk/by-scsid/emc-vol-*'
31SYSFS_BLOCK = '/sys/class/block'
33DRIVER_BLACKLIST = ['^(s|p|)ata_.*', '^ahci$', '^pdc_adma$', '^iscsi_tcp$', '^usb-storage$']
35INVALID_DEVICE_NAME = ''
38def getManufacturer(s):
39 (rc, stdout, stderr) = util.doexec(['/sbin/modinfo', '-d', s])
40 if stdout: 40 ↛ 43line 40 didn't jump to line 43, because the condition on line 40 was never false
41 return stdout.strip()
42 else:
43 return "Unknown"
46def update_devs_dict(devs, dev, entry):
47 if dev != INVALID_DEVICE_NAME:
48 devs[dev] = entry
51def adapters(filterstr="any"):
52 dict = {}
53 devs = {}
54 adt = {}
55 fcoe_eth_info = {}
56 fcoe_port_info = []
58 fcoe_port_info = fcoelib.parse_fcoe_port_name_info()
59 if filterstr == "fcoe": 59 ↛ 60line 59 didn't jump to line 60, because the condition on line 59 was never true
60 fcoe_eth_info = fcoelib.parse_fcoe_eth_info()
62 for a in os.listdir(SYSFS_PATH1):
63 proc = match_hbadevs(a, filterstr)
64 if not proc: 64 ↛ 65line 64 didn't jump to line 65, because the condition on line 64 was never true
65 continue
67 #Special casing for fcoe
68 port_name_path = os.path.join(SYSFS_PATH1, a, 'device', \
69 'fc_host', a, 'port_name')
70 port_name_path_exists = os.path.exists(port_name_path)
71 util.SMlog("Port name path exists %s" % port_name_path_exists)
72 if filterstr == "fcoe" and not port_name_path_exists: 72 ↛ 73line 72 didn't jump to line 73, because the condition on line 72 was never true
73 continue
74 if port_name_path_exists: 74 ↛ 75line 74 didn't jump to line 75, because the condition on line 74 was never true
75 port_name = _get_port_name(port_name_path)
76 #If we are probing for fcoe luns/ adapters and if the port name
77 #in /sys/class/scsi_host/a/device/fc_host/a/port_name does not match
78 #one in the output of 'fcoeadm -i', then we shouldn't display that
79 #lun/adapter.
80 #On the other hand, if we are probing for hba luns, and if the
81 #port name in /sys/class/scsi_host/a/device/fc_host/a/port_name
82 #matches one in the output of 'fcoeadm -i', then we shouldn't
83 #display that lun/adapter, because that would have been discovered
84 #via the FCoE protocol.
85 if (filterstr == "fcoe" and port_name not in fcoe_port_info) or \
86 (filterstr != "fcoe" and port_name in fcoe_port_info):
87 continue
89 adt[a] = proc
90 id = a.replace("host", "")
91 scsiutil.rescan([id])
92 emulex = False
93 paths = []
94 if proc == "lpfc":
95 emulex = True
96 paths.append(SYSFS_PATH3)
97 else:
98 for p in [os.path.join(SYSFS_PATH1, a, "device", "session*"), os.path.join(SYSFS_PATH1, a, "device"), \
99 os.path.join(SYSFS_PATH2, "%s:*" % id)]:
100 paths += glob.glob(p)
102 if not len(paths): 102 ↛ 103line 102 didn't jump to line 103, because the condition on line 102 was never true
103 continue
104 for path in paths:
105 for i in filter(match_targets, os.listdir(path)):
106 tgt = i.replace('target', '')
107 if emulex: 107 ↛ 110line 107 didn't jump to line 110, because the condition on line 107 was never false
108 sysfs = os.path.join(SYSFS_PATH3, i, "device")
109 else:
110 sysfs = SYSFS_PATH2
111 for lun in os.listdir(sysfs):
112 if not match_LUNs(lun, tgt): 112 ↛ 113line 112 didn't jump to line 113, because the condition on line 112 was never true
113 continue
114 if emulex: 114 ↛ 117line 114 didn't jump to line 117, because the condition on line 114 was never false
115 dir = os.path.join(sysfs, lun)
116 else:
117 dir = os.path.join(sysfs, lun, "device")
118 (dev, entry) = _extract_dev(dir, proc, id, lun)
119 update_devs_dict(devs, dev, entry)
120 # for new qlogic sysfs layout (rport under device, then target)
121 for i in filter(match_rport, os.listdir(path)): 121 ↛ 122line 121 didn't jump to line 122, because the loop on line 121 never started
122 newpath = os.path.join(path, i)
123 for j in filter(match_targets, os.listdir(newpath)):
124 tgt = j.replace('target', '')
125 sysfs = SYSFS_PATH2
126 for lun in os.listdir(sysfs):
127 if not match_LUNs(lun, tgt):
128 continue
129 #Special casing for fcoe, populating eth information
130 eth = ""
131 if i in fcoe_eth_info.keys():
132 eth = fcoe_eth_info[i]
133 dir = os.path.join(sysfs, lun, "device")
134 (dev, entry) = _extract_dev(dir, proc, id, lun, eth)
135 update_devs_dict(devs, dev, entry)
137 # for new mptsas sysfs entries, check for phy* node
138 for i in filter(match_phy, os.listdir(path)): 138 ↛ 139line 138 didn't jump to line 139, because the loop on line 138 never started
139 (target, lunid) = i.replace('phy-', '').split(':')
140 tgt = "%s:0:0:%s" % (target, lunid)
141 sysfs = SYSFS_PATH2
142 for lun in os.listdir(sysfs):
143 if not match_LUNs(lun, tgt):
144 continue
145 dir = os.path.join(sysfs, lun, "device")
146 (dev, entry) = _extract_dev(dir, proc, id, lun)
147 update_devs_dict(devs, dev, entry)
148 if path.startswith(SYSFS_PATH2):
149 os.path.join(path, "device", "block:*")
150 dev = _extract_dev_name(os.path.join(path, 'device'))
151 if dev in devs: 151 ↛ 152line 151 didn't jump to line 152, because the condition on line 151 was never true
152 continue
153 hbtl = os.path.basename(path)
154 (h, b, t, l) = hbtl.split(':')
155 entry = {'procname': proc, 'host': id, 'target': l}
156 update_devs_dict(devs, dev, entry)
158 dict['devs'] = devs
159 dict['adt'] = adt
160 return dict
163def _get_port_name(port_name_path):
164 port_name = 0
165 try:
166 f = open(port_name_path, 'r')
167 try:
168 line = f.readline()[:-1]
169 if not line in ['<NULL>', '(NULL)', '']:
170 port_name = int(line, 16)
171 util.SMlog("Port Name in sysfs is %d" % port_name)
172 finally:
173 f.close()
174 except IOError:
175 pass
176 return port_name
179def _get_driver_name(scsihost):
180 driver_name = 'Unknown'
181 if os.path.exists(os.path.join(SYSFS_PATH1, scsihost, 'fnic_state')): 181 ↛ 182line 181 didn't jump to line 182, because the condition on line 181 was never true
182 driver_name = 'fnic'
183 if os.path.exists(os.path.join(SYSFS_PATH1, scsihost, 'lpfc_fcp_class')): 183 ↛ 184line 183 didn't jump to line 184, because the condition on line 183 was never true
184 driver_name = 'lpfc'
185 if os.path.exists(os.path.join(SYSFS_PATH1, scsihost, '84xx_fw_version')): 185 ↛ 186line 185 didn't jump to line 186, because the condition on line 185 was never true
186 driver_name = 'qla2xxx'
187 if 'Unknown' == driver_name: 187 ↛ 200line 187 didn't jump to line 200, because the condition on line 187 was never false
188 namepath = os.path.join(SYSFS_PATH1, scsihost, 'driver_name')
189 if not os.path.exists(namepath): 189 ↛ 191line 189 didn't jump to line 191, because the condition on line 189 was never false
190 namepath = os.path.join(SYSFS_PATH1, scsihost, 'proc_name')
191 if os.path.exists(namepath): 191 ↛ 192line 191 didn't jump to line 192, because the condition on line 191 was never true
192 try:
193 f = open(namepath, 'r')
194 line = f.readline()[:-1]
195 f.close()
196 if not line in ['<NULL>', '(NULL)', '']:
197 driver_name = line
198 except IOError:
199 pass
200 if 'Unknown' == driver_name: 200 ↛ 211line 200 didn't jump to line 211, because the condition on line 200 was never false
201 ueventpath = os.path.join(SYSFS_PATH1, scsihost, 'uevent')
202 if os.path.exists(ueventpath): 202 ↛ 203line 202 didn't jump to line 203, because the condition on line 202 was never true
203 try:
204 f = open(ueventpath, 'r')
205 for line in f:
206 if line.startswith('PHYSDEVDRIVER='):
207 driver_name = line.replace('PHYSDEVDRIVER=', '').strip()
208 f.close()
209 except IOError:
210 pass
211 return driver_name
214def _parseHostId(str):
215 id = str.split()
216 val = "%s:%s:%s" % (id[1], id[3], id[5])
217 return val.replace(',', '')
220def match_hbadevs(s, filterstr):
221 driver_name = _get_driver_name(s)
222 if match_host(s) and not match_blacklist(driver_name) \ 222 ↛ 227line 222 didn't jump to line 227, because the condition on line 222 was never false
223 and (filterstr == "any" or filterstr == "fcoe" or \
224 match_filterstr(filterstr, driver_name)):
225 return driver_name
226 else:
227 return ""
230def match_blacklist(driver_name):
231 return re.search("(" + ")|(".join(DRIVER_BLACKLIST) + ")", driver_name)
234def match_filterstr(filterstr, driver_name):
235 return re.search("^%s" % filterstr, driver_name)
238def match_host(s):
239 return re.search("^host[0-9]", s)
242def match_rport(s):
243 regex = re.compile("^rport-*")
244 return regex.search(s, 0)
247def match_targets(s):
248 regex = re.compile("^target[0-9]")
249 return regex.search(s, 0)
252def match_phy(s):
253 regex = re.compile("^phy-*")
254 return regex.search(s, 0)
257def match_LUNs(s, prefix):
258 regex = re.compile("^%s" % prefix)
259 return regex.search(s, 0)
262def match_dev(s):
263 regex = re.compile("^block:")
264 return regex.search(s, 0)
267def _extract_dev_name(device_dir):
268 """Returns the name of the block device from sysfs e.g. 'sda'"""
269 return _get_block_device_name_with_kernel_3x(device_dir)
272def _get_block_device_name_with_kernel_3x(device_dir):
273 devs = glob.glob(os.path.join(device_dir, 'block/*'))
274 if len(devs):
275 # prune path to extract the device name
276 return os.path.basename(devs[0])
277 else:
278 return INVALID_DEVICE_NAME
281def _extract_dev(device_dir, procname, host, target, eths=""):
282 """Returns device name and creates dictionary entry for it"""
283 dev = _extract_dev_name(device_dir)
284 entry = {}
285 entry['procname'] = procname
286 entry['host'] = host
287 entry['target'] = target
288 entry['eth'] = eths
289 return (dev, entry)
292def _add_host_parameters_to_adapter(dom, adapter, host_class, host_id,
293 parameters):
294 """Adds additional information about the adapter to the the adapter node"""
295 host_path = os.path.join('/sys/class/', host_class, 'host%s' % (host_id))
296 if os.path.exists(host_path):
297 host_entry = dom.createElement(host_class)
298 adapter.appendChild(host_entry)
299 for parameter in parameters:
300 try:
301 filehandle = open(os.path.join(host_path, parameter))
302 parameter_value = filehandle.read(512).strip()
303 filehandle.close()
304 if parameter_value: 304 ↛ 299line 304 didn't jump to line 299, because the condition on line 304 was never false
305 entry = dom.createElement(parameter)
306 host_entry.appendChild(entry)
307 text_node = dom.createTextNode(parameter_value)
308 entry.appendChild(text_node)
309 except IOError:
310 pass
313def scan(srobj):
314 systemrootID = util.getrootdevID()
315 hbadict = srobj.hbadict
316 hbas = srobj.hbas
317 dom = xml.dom.minidom.Document()
318 e = dom.createElement("Devlist")
319 dom.appendChild(e)
321 if not os.path.exists(DEVPATH):
322 return dom.toprettyxml()
324 devs = srobj.devs
325 vdis = {}
327 for key in hbadict:
328 hba = hbadict[key]
329 path = os.path.join("/dev", key)
330 realpath = path
332 obj = srobj.vdi("")
333 try:
334 obj._query(realpath, devs[realpath][4])
335 except:
336 continue
338 # Test for root dev or existing PBD
339 if len(obj.SCSIid) and len(systemrootID) and util.match_scsiID(obj.SCSIid, systemrootID):
340 util.SMlog("Ignoring root device %s" % realpath)
341 continue
342 elif util.test_SCSIid(srobj.session, None, obj.SCSIid):
343 util.SMlog("SCSIid in use, ignoring (%s)" % obj.SCSIid)
344 continue
345 elif realpath not in devs:
346 continue
348 ids = devs[realpath]
349 obj.adapter = ids[1]
350 obj.channel = ids[2]
351 obj.id = ids[3]
352 obj.lun = ids[4]
353 obj.hba = hba['procname']
354 if 'eth' in hba and hba['eth']:
355 obj.eth = hba['eth']
356 obj.numpaths = 1
357 if obj.SCSIid in vdis:
358 vdis[obj.SCSIid].numpaths += 1
359 vdis[obj.SCSIid].path += " [%s]" % key
360 else:
361 vdis[obj.SCSIid] = obj
363 for key in vdis: 363 ↛ 364line 363 didn't jump to line 364, because the loop on line 363 never started
364 obj = vdis[key]
365 d = dom.createElement("BlockDevice")
366 e.appendChild(d)
368 for attr in ['path', 'numpaths', 'SCSIid', 'vendor', 'serial', 'size', 'adapter', 'channel', 'id', 'lun', 'hba', 'eth']:
369 try:
370 aval = getattr(obj, attr)
371 except AttributeError:
372 if attr in ['eth']:
373 continue
374 raise xs_errors.XenError('InvalidArg',
375 opterr='Missing required field [%s]' % attr)
376 entry = dom.createElement(attr)
377 d.appendChild(entry)
378 textnode = dom.createTextNode(str(aval))
379 entry.appendChild(textnode)
381 pfx_vols = getattr(srobj, 'powerflex_devices', {})
382 for pfx_vol in pfx_vols.values():
383 dev_elem = dom.createElement("BlockDevice")
384 e.appendChild(dev_elem)
385 for k, v in pfx_vol.items():
386 entry = dom.createElement(str(k))
387 dev_elem.appendChild(entry)
388 text_node = dom.createTextNode(str(v))
389 entry.appendChild(text_node)
391 for key in hbas:
392 a = dom.createElement("Adapter")
393 e.appendChild(a)
394 entry = dom.createElement('host')
395 a.appendChild(entry)
396 textnode = dom.createTextNode(key)
397 entry.appendChild(textnode)
399 entry = dom.createElement('name')
400 a.appendChild(entry)
401 textnode = dom.createTextNode(hbas[key])
402 entry.appendChild(textnode)
404 entry = dom.createElement('manufacturer')
405 a.appendChild(entry)
406 textnode = dom.createTextNode(getManufacturer(hbas[key]))
407 entry.appendChild(textnode)
409 id = key.replace("host", "")
410 entry = dom.createElement('id')
411 a.appendChild(entry)
412 textnode = dom.createTextNode(id)
413 entry.appendChild(textnode)
415 _add_host_parameters_to_adapter(dom, a, 'fc_host', id,
416 ['node_name', 'port_name',
417 'port_state', 'speed',
418 'supported_speeds'])
419 _add_host_parameters_to_adapter(dom, a, 'iscsi_host', id,
420 ['hwaddress', 'initiatorname',
421 'ipaddress', 'port_speed',
422 'port_state'])
424 return dom.toprettyxml()
427def check_iscsi(adapter):
428 ret = False
429 str = "host%s" % adapter
430 try:
431 filename = os.path.join('/sys/class/scsi_host', str, 'proc_name')
432 f = open(filename, 'r')
433 if f.readline().find("iscsi_tcp") != -1:
434 ret = True
435 except:
436 pass
437 return ret
440def match_nonpartitions(s):
441 regex = re.compile("-part[0-9]")
442 if not regex.search(s, 0):
443 return True
446def powerflex_devices():
447 devices = {}
449 for vol in glob.glob(PFX_PATH):
450 try:
451 SCSIid = os.path.basename(vol)
452 path = os.path.realpath(os.path.join(vol, 'scini'))
453 dev_name = os.path.basename(path)
455 with open(os.path.join(SYSFS_BLOCK, dev_name, 'size')) as f:
456 size_in_blocks = int(f.readline().strip())
458 with open(os.path.join(SYSFS_BLOCK, dev_name,
459 'queue', 'logical_block_size')) as f:
460 blocksize = int(f.readline().strip())
462 devices[SCSIid] = {
463 'SCSIid': SCSIid,
464 'path': path,
465 'vendor': 'emc',
466 'size': size_in_blocks * blocksize,
467 }
468 except (OSError, ValueError) as exc:
469 util.SMlog("Skipping PowerFlex volume %s during probe: %s",
470 vol, exc)
472 return devices