Hide keyboard shortcuts

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 

15 

16import os 

17import re 

18import scsiutil 

19import util 

20import xml.dom.minidom 

21import xs_errors 

22import glob 

23import fcoelib 

24 

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' 

30 

31DRIVER_BLACKLIST = ['^(s|p|)ata_.*', '^ahci$', '^pdc_adma$', '^iscsi_tcp$', '^usb-storage$'] 

32 

33INVALID_DEVICE_NAME = '' 

34 

35 

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" 

42 

43 

44def update_devs_dict(devs, dev, entry): 

45 if dev != INVALID_DEVICE_NAME: 

46 devs[dev] = entry 

47 

48 

49def adapters(filterstr="any"): 

50 dict = {} 

51 devs = {} 

52 adt = {} 

53 fcoe_eth_info = {} 

54 fcoe_port_info = [] 

55 

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() 

59 

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 

64 

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 

86 

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) 

99 

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) 

134 

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) 

155 

156 dict['devs'] = devs 

157 dict['adt'] = adt 

158 return dict 

159 

160 

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 

175 

176 

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 

210 

211 

212def _parseHostId(str): 

213 id = str.split() 

214 val = "%s:%s:%s" % (id[1], id[3], id[5]) 

215 return val.replace(',', '') 

216 

217 

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 "" 

226 

227 

228def match_blacklist(driver_name): 

229 return re.search("(" + ")|(".join(DRIVER_BLACKLIST) + ")", driver_name) 

230 

231 

232def match_filterstr(filterstr, driver_name): 

233 return re.search("^%s" % filterstr, driver_name) 

234 

235 

236def match_host(s): 

237 return re.search("^host[0-9]", s) 

238 

239 

240def match_rport(s): 

241 regex = re.compile("^rport-*") 

242 return regex.search(s, 0) 

243 

244 

245def match_targets(s): 

246 regex = re.compile("^target[0-9]") 

247 return regex.search(s, 0) 

248 

249 

250def match_phy(s): 

251 regex = re.compile("^phy-*") 

252 return regex.search(s, 0) 

253 

254 

255def match_LUNs(s, prefix): 

256 regex = re.compile("^%s" % prefix) 

257 return regex.search(s, 0) 

258 

259 

260def match_dev(s): 

261 regex = re.compile("^block:") 

262 return regex.search(s, 0) 

263 

264 

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) 

268 

269 

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 

277 

278 

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) 

288 

289 

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 

309 

310 

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) 

318 

319 if not os.path.exists(DEVPATH): 

320 return dom.toprettyxml() 

321 

322 devs = srobj.devs 

323 vdis = {} 

324 

325 for key in hbadict: 

326 hba = hbadict[key] 

327 path = os.path.join("/dev", key) 

328 realpath = path 

329 

330 obj = srobj.vdi("") 

331 try: 

332 obj._query(realpath, devs[realpath][4]) 

333 except: 

334 continue 

335 

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 

345 

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 

360 

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) 

365 

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) 

378 

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) 

386 

387 entry = dom.createElement('name') 

388 a.appendChild(entry) 

389 textnode = dom.createTextNode(hbas[key]) 

390 entry.appendChild(textnode) 

391 

392 entry = dom.createElement('manufacturer') 

393 a.appendChild(entry) 

394 textnode = dom.createTextNode(getManufacturer(hbas[key])) 

395 entry.appendChild(textnode) 

396 

397 id = key.replace("host", "") 

398 entry = dom.createElement('id') 

399 a.appendChild(entry) 

400 textnode = dom.createTextNode(id) 

401 entry.appendChild(textnode) 

402 

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']) 

411 

412 return dom.toprettyxml() 

413 

414 

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 

426 

427 

428def match_nonpartitions(s): 

429 regex = re.compile("-part[0-9]") 

430 if not regex.search(s, 0): 

431 return True