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

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

241 

242 

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

251 

252 

253def match_blacklist(driver_name): 

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

255 

256 

257def match_filterstr(filterstr, driver_name): 

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

259 

260 

261def match_host(s): 

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

263 

264 

265def match_rport(s): 

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

267 return regex.search(s, 0) 

268 

269 

270def match_targets(s): 

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

272 return regex.search(s, 0) 

273 

274 

275def match_phy(s): 

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

277 return regex.search(s, 0) 

278 

279 

280def match_LUNs(s, prefix): 

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

282 return regex.search(s, 0) 

283 

284 

285def match_dev(s): 

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

287 return regex.search(s, 0) 

288 

289 

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) 

293 

294 

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 

302 

303 

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) 

313 

314 

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 

334 

335 

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) 

343 

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

345 return dom.toprettyxml() 

346 

347 devs = srobj.devs 

348 vdis = {} 

349 

350 for key in hbadict: 

351 hba = hbadict[key] 

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

353 realpath = path 

354 

355 obj = srobj.vdi("") 

356 try: 

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

358 except: 

359 continue 

360 

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 

370 

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 

398 

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) 

403 

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) 

416 

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) 

424 

425 entry = dom.createElement('name') 

426 a.appendChild(entry) 

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

428 entry.appendChild(textnode) 

429 

430 entry = dom.createElement('manufacturer') 

431 a.appendChild(entry) 

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

433 entry.appendChild(textnode) 

434 

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

436 entry = dom.createElement('id') 

437 a.appendChild(entry) 

438 textnode = dom.createTextNode(id) 

439 entry.appendChild(textnode) 

440 

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

449 

450 return dom.toprettyxml() 

451 

452 

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 

464 

465 

466def match_nonpartitions(s): 

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

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

469 return True