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' 

30PFX_PATH = '/dev/disk/by-scsid/emc-vol-*' 

31SYSFS_BLOCK = '/sys/class/block' 

32 

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

34 

35INVALID_DEVICE_NAME = '' 

36 

37 

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" 

44 

45 

46def update_devs_dict(devs, dev, entry): 

47 if dev != INVALID_DEVICE_NAME: 

48 devs[dev] = entry 

49 

50 

51def adapters(filterstr="any"): 

52 dict = {} 

53 devs = {} 

54 adt = {} 

55 fcoe_eth_info = {} 

56 fcoe_port_info = [] 

57 

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

61 

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 

66 

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 

88 

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) 

101 

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) 

136 

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) 

157 

158 dict['devs'] = devs 

159 dict['adt'] = adt 

160 return dict 

161 

162 

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 

177 

178 

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 

212 

213 

214def _parseHostId(str): 

215 id = str.split() 

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

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

218 

219 

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

228 

229 

230def match_blacklist(driver_name): 

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

232 

233 

234def match_filterstr(filterstr, driver_name): 

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

236 

237 

238def match_host(s): 

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

240 

241 

242def match_rport(s): 

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

244 return regex.search(s, 0) 

245 

246 

247def match_targets(s): 

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

249 return regex.search(s, 0) 

250 

251 

252def match_phy(s): 

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

254 return regex.search(s, 0) 

255 

256 

257def match_LUNs(s, prefix): 

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

259 return regex.search(s, 0) 

260 

261 

262def match_dev(s): 

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

264 return regex.search(s, 0) 

265 

266 

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) 

270 

271 

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 

279 

280 

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) 

290 

291 

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 

311 

312 

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) 

320 

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

322 return dom.toprettyxml() 

323 

324 devs = srobj.devs 

325 vdis = {} 

326 

327 for key in hbadict: 

328 hba = hbadict[key] 

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

330 realpath = path 

331 

332 obj = srobj.vdi("") 

333 try: 

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

335 except: 

336 continue 

337 

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 

347 

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 

362 

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) 

367 

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) 

380 

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) 

390 

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) 

398 

399 entry = dom.createElement('name') 

400 a.appendChild(entry) 

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

402 entry.appendChild(textnode) 

403 

404 entry = dom.createElement('manufacturer') 

405 a.appendChild(entry) 

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

407 entry.appendChild(textnode) 

408 

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

410 entry = dom.createElement('id') 

411 a.appendChild(entry) 

412 textnode = dom.createTextNode(id) 

413 entry.appendChild(textnode) 

414 

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

423 

424 return dom.toprettyxml() 

425 

426 

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 

438 

439 

440def match_nonpartitions(s): 

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

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

443 return True 

444 

445 

446def powerflex_devices(): 

447 devices = {} 

448 

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) 

454 

455 with open(os.path.join(SYSFS_BLOCK, dev_name, 'size')) as f: 

456 size_in_blocks = int(f.readline().strip()) 

457 

458 with open(os.path.join(SYSFS_BLOCK, dev_name, 

459 'queue', 'logical_block_size')) as f: 

460 blocksize = int(f.readline().strip()) 

461 

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) 

471 

472 return devices