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#!/usr/bin/python3 

2# 

3# Copyright (C) Citrix Systems Inc. 

4# 

5# This program is free software; you can redistribute it and/or modify 

6# it under the terms of the GNU Lesser General Public License as published 

7# by the Free Software Foundation; version 2.1 only. 

8# 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU Lesser General Public License for more details. 

13# 

14# You should have received a copy of the GNU Lesser General Public License 

15# along with this program; if not, write to the Free Software Foundation, Inc., 

16# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 

17# 

18# Miscellaneous scsi utility functions 

19# 

20 

21import util 

22import os 

23import re 

24import xs_errors 

25import base64 

26import time 

27import errno 

28import glob 

29import mpath_cli 

30import traceback 

31 

32PREFIX_LEN = 4 

33SUFFIX_LEN = 12 

34SECTOR_SHIFT = 9 

35SCSI_ID_BIN = '/usr/lib/udev/scsi_id' 

36 

37 

38def gen_hash(st, len): 

39 hs = 0 

40 for i in st: 

41 hs = ord(i) + (hs << 6) + (hs << 16) - hs 

42 return str(hs)[0:len] 

43 

44 

45def gen_uuid_from_serial(iqn, serial): 

46 if len(serial) < SUFFIX_LEN: 

47 raise util.CommandException(1) 

48 prefix = gen_hash(iqn, PREFIX_LEN) 

49 suffix = gen_hash(serial, SUFFIX_LEN) 

50 str = prefix.encode("hex") + suffix.encode("hex") 

51 return str[0:8] + '-' + str[8:12] + '-' + str[12:16] + '-' + str[16:20] + '-' + str[20:32] 

52 

53 

54def gen_serial_from_uuid(iqn, uuid): 

55 str = uuid.replace('-', '') 

56 prefix = gen_hash(iqn, PREFIX_LEN) 

57 if str[0:(PREFIX_LEN * 2)].decode("hex") != prefix: 

58 raise util.CommandException(1) 

59 return str[(PREFIX_LEN * 2):].decode("hex") 

60 

61 

62def getsize(path): 

63 dev = getdev(path) 

64 sysfs = os.path.join('/sys/block', dev, 'size') 

65 size = 0 

66 if os.path.exists(sysfs): 

67 try: 

68 f = open(sysfs, 'r') 

69 size = (int(f.readline()) << SECTOR_SHIFT) 

70 f.close() 

71 except: 

72 pass 

73 return size 

74 

75 

76def getuniqueserial(path): 

77 dev = getdev(path) 

78 try: 

79 cmd = ["md5sum"] 

80 txt = util.pread3(cmd, getSCSIid(path)) 

81 return txt.split(' ')[0] 

82 except: 

83 return '' 

84 

85 

86def gen_uuid_from_string(src_string): 

87 if len(src_string) < (PREFIX_LEN + SUFFIX_LEN): 

88 raise util.CommandException(1) 

89 return (src_string[0:8] + '-' + 

90 src_string[8:12] + '-' + 

91 src_string[12:16] + '-' + 

92 src_string[16:20] + '-' + 

93 src_string[20:32]) 

94 

95 

96def SCSIid_sanitise(str): 

97 text = str.strip() 

98 return re.sub(r"\s+", "_", text) 

99 

100 

101def getSCSIid(path): 

102 """Get the SCSI id of a block device 

103 

104 Input: 

105 path -- (str) path to block device; can be symlink 

106 

107 Return: 

108 scsi_id -- (str) the device's SCSI id 

109 

110 Raise: 

111 util.CommandException 

112 """ 

113 

114 if not path.startswith('/dev/'): 114 ↛ 115line 114 didn't jump to line 115, because the condition on line 114 was never true

115 util.SMlog("getSCSIid: fixing invalid input {}".format(path), 

116 priority=util.LOG_WARNING) 

117 path = '/dev/' + path.lstrip('/') 

118 

119 stdout = util.pread2([SCSI_ID_BIN, '-g', '--device', path]) 

120 

121 return SCSIid_sanitise(stdout) 

122 

123 

124def compareSCSIid_2_6_18(SCSIid, path): 

125 serial = getserial(path) 

126 len_serial = len(serial) 

127 if (len_serial == 0) or (len_serial > (len(SCSIid) - 1)): 

128 return False 

129 list_SCSIid = list(SCSIid) 

130 list_serial = list_SCSIid[1:(len_serial + 1)] 

131 serial_2_6_18 = ''.join(list_serial) 

132 if (serial == serial_2_6_18): 

133 return True 

134 else: 

135 return False 

136 

137 

138def getserial(path): 

139 dev = os.path.join('/dev', getdev(path)) 

140 try: 

141 cmd = ["sginfo", "-s", dev] 

142 text = re.sub(r"\s+", "", util.pread2(cmd)) 

143 except: 

144 raise xs_errors.XenError('EIO', \ 

145 opterr='An error occured querying device serial number [%s]' \ 

146 % dev) 

147 try: 

148 return text.split("'")[1] 

149 except: 

150 return '' 

151 

152 

153def getmanufacturer(path): 

154 cmd = ["sginfo", "-M", path] 

155 try: 

156 for line in filter(match_vendor, util.pread2(cmd).split('\n')): 

157 return line.replace(' ', '').split(':')[-1] 

158 except: 

159 return '' 

160 

161 

162def cacheSCSIidentifiers(): 

163 SCSI = {} 

164 SYS_PATH = "/dev/disk/by-scsibus/*" 

165 for node in glob.glob(SYS_PATH): 

166 if not re.match(r'.*-\d+:\d+:\d+:\d+$', node): 166 ↛ 167line 166 didn't jump to line 167, because the condition on line 166 was never true

167 continue 

168 dev = os.path.realpath(node) 

169 HBTL = os.path.basename(node).split("-")[-1].split(":") 

170 line = "NONE %s %s %s %s 0 %s" % \ 

171 (HBTL[0], HBTL[1], HBTL[2], HBTL[3], dev) 

172 ids = line.split() 

173 SCSI[ids[6]] = ids 

174 return SCSI 

175 

176 

177def scsi_dev_ctrl(ids, cmd): 

178 f = None 

179 for i in range(0, 10): 

180 try: 

181 str = "scsi %s-single-device %s %s %s %s" % \ 

182 (cmd, ids[1], ids[2], ids[3], ids[4]) 

183 util.SMlog(str) 

184 f = open('/proc/scsi/scsi', 'w') 

185 print(str, file=f) 

186 f.close() 

187 return 

188 except IOError as e: 

189 util.SMlog("SCSI_DEV_CTRL: Failure, %s [%d]" % (e.strerror, e.errno)) 

190 if f is not None: 

191 f.close() 

192 f = None 

193 if e.errno == errno.ENXIO: 

194 util.SMlog("Device has disappeared already") 

195 return 

196 time.sleep(6) 

197 continue 

198 raise xs_errors.XenError('EIO', \ 

199 opterr='An error occured during the scsi operation') 

200 

201 

202def getdev(path): 

203 realpath = os.path.realpath(path) 

204 if match_dm(realpath): 204 ↛ 205line 204 didn't jump to line 205, because the condition on line 204 was never true

205 newpath = realpath.replace("/dev/mapper/", "/dev/disk/by-id/scsi-") 

206 else: 

207 newpath = path 

208 return os.path.realpath(newpath).split('/')[-1] 

209 

210 

211def get_devices_by_SCSIid(SCSIid): 

212 devices = os.listdir(os.path.join('/dev/disk/by-scsid', SCSIid)) 

213 if 'mapper' in devices: 

214 devices.remove('mapper') 

215 return devices 

216 

217 

218def rawdev(dev): 

219 device = getdev(dev) 

220 if device.startswith('dm-') and device[3:].isdigit(): 

221 return device 

222 

223 return re.sub('[0-9]*$', '', device) 

224 

225 

226def getSessionID(path): 

227 for line in filter(match_session, util.listdir(path)): 

228 return line.split('-')[-1] 

229 

230 

231def match_session(s): 

232 regex = re.compile("^SESSIONID-") 

233 return regex.search(s, 0) 

234 

235 

236def match_vendor(s): 

237 regex = re.compile("^Vendor:") 

238 return regex.search(s, 0) 

239 

240 

241def match_dm(s): 

242 regex = re.compile("mapper/") 

243 return regex.search(s, 0) 

244 

245 

246def match_sd(s): 

247 regex = re.compile("/dev/sd") 

248 return regex.search(s, 0) 

249 

250 

251def _isSCSIdev(dev): 

252 if match_dm(dev): 

253 path = dev.replace("/dev/mapper/", "/dev/disk/by-id/scsi-") 

254 else: 

255 path = dev 

256 return match_sd(os.path.realpath(path)) 

257 

258 

259def add_serial_record(session, sr_ref, devstring): 

260 try: 

261 conf = session.xenapi.SR.get_sm_config(sr_ref) 

262 conf['devserial'] = devstring 

263 session.xenapi.SR.set_sm_config(sr_ref, conf) 

264 except: 

265 pass 

266 

267 

268def get_serial_record(session, sr_ref): 

269 try: 

270 conf = session.xenapi.SR.get_sm_config(sr_ref) 

271 return conf['devserial'] 

272 except: 

273 return "" 

274 

275 

276def devlist_to_serialstring(devlist): 

277 serial = '' 

278 for dev in devlist: 

279 try: 

280 devserial = "scsi-%s" % getSCSIid(dev) 

281 if not len(devserial) > 0: 

282 continue 

283 if len(serial): 

284 serial += ',' 

285 serial += devserial 

286 except: 

287 pass 

288 

289 return serial 

290 

291 

292def gen_synthetic_page_data(uuid): 

293 # For generating synthetic page data for non-raw LUNs 

294 # we set the vendor ID to XENSRC 

295 # Note that the Page 80 serial number must be limited 

296 # to 16 characters 

297 page80 = "" 

298 page80 += "\x00\x80" 

299 page80 += "\x00\x12" 

300 page80 += uuid[0:16] 

301 page80 += " " 

302 

303 page83 = "" 

304 page83 += "\x00\x83" 

305 page83 += "\x00\x31" 

306 page83 += "\x02\x01\x00\x2d" 

307 page83 += "XENSRC " 

308 page83 += uuid 

309 page83 += " " 

310 return ["", base64.b64encode(str.encode(page80)).decode(), 

311 base64.b64encode(str.encode(page83)).decode()] 

312 

313 

314def gen_raw_page_data(path): 

315 default = "" 

316 page80 = "" 

317 page83 = "" 

318 try: 

319 cmd = ["sg_inq", "-r", path] 

320 text = util.pread2(cmd) 

321 default = base64.b64encode(text) 

322 

323 cmd = ["sg_inq", "--page=0x80", "-r", path] 

324 text = util.pread2(cmd) 

325 page80 = base64.b64encode(text) 

326 

327 cmd = ["sg_inq", "--page=0x83", "-r", path] 

328 text = util.pread2(cmd) 

329 page83 = base64.b64encode(text) 

330 except: 

331 pass 

332 return [default, page80, page83] 

333 

334 

335def update_XS_SCSIdata(vdi_uuid, data): 

336 # XXX: PR-1255: passing through SCSI data doesn't make sense when 

337 # it will change over storage migration. It also doesn't make sense 

338 # to preserve one array's identity and copy it when a VM moves to 

339 # a new array because the drivers in the VM may attempt to contact 

340 # the original array, fail and bluescreen. 

341 

342 xenstore_data = {} 

343 xenstore_data["vdi-uuid"] = vdi_uuid 

344 if len(data[0]): 344 ↛ 345line 344 didn't jump to line 345, because the condition on line 344 was never true

345 xenstore_data["scsi/0x12/default"] = data[0] 

346 

347 if len(data[1]): 347 ↛ 350line 347 didn't jump to line 350, because the condition on line 347 was never false

348 xenstore_data["scsi/0x12/0x80"] = data[1] 

349 

350 if len(data[2]): 350 ↛ 353line 350 didn't jump to line 353, because the condition on line 350 was never false

351 xenstore_data["scsi/0x12/0x83"] = data[2] 

352 

353 return xenstore_data 

354 

355 

356def rescan(ids, fullrescan=True): 

357 for id in ids: 

358 refresh_HostID(id, fullrescan) 

359 

360 

361def _genHostList(procname): 

362 # loop through and check all adapters 

363 ids = [] 

364 try: 

365 for dir in util.listdir('/sys/class/scsi_host'): 

366 filename = os.path.join('/sys/class/scsi_host', dir, 'proc_name') 

367 if os.path.exists(filename): 

368 f = open(filename, 'r') 

369 if f.readline().find(procname) != -1: 

370 ids.append(dir.replace("host", "")) 

371 f.close() 

372 except: 

373 pass 

374 return ids 

375 

376 

377def _genReverseSCSIidmap(SCSIid, pathname="scsibus"): 

378 util.SMlog("map_by_scsibus: sid=%s" % SCSIid) 

379 

380 devices = [] 

381 for link in glob.glob('/dev/disk/by-%s/%s-*' % (pathname, SCSIid)): 

382 realpath = os.path.realpath(link) 

383 if os.path.exists(realpath): 

384 devices.append(realpath) 

385 return devices 

386 

387 

388def _genReverseSCSidtoLUNidmap(SCSIid): 

389 devices = [] 

390 for link in glob.glob('/dev/disk/by-scsibus/%s-*' % SCSIid): 

391 devices.append(link.split('-')[-1]) 

392 return devices 

393 

394 

395def _dosgscan(): 

396 regex = re.compile(r"([^:]*):\s+scsi([0-9]+)\s+channel=([0-9]+)\s+id=([0-9]+)\s+lun=([0-9]+)") 

397 scan = util.pread2(["/usr/bin/sg_scan"]).split('\n') 

398 sgs = [] 

399 for line in scan: 

400 m = regex.match(line) 

401 if m: 

402 device = m.group(1) 

403 host = m.group(2) 

404 channel = m.group(3) 

405 sid = m.group(4) 

406 lun = m.group(5) 

407 sgs.append([device, host, channel, sid, lun]) 

408 return sgs 

409 

410 

411def refresh_HostID(HostID, fullrescan): 

412 LUNs = glob.glob('/sys/class/scsi_disk/%s*' % HostID) 

413 li = [] 

414 for l in LUNs: 

415 chan = re.sub(":[0-9]*$", '', os.path.basename(l)) 

416 if chan not in li: 416 ↛ 414line 416 didn't jump to line 414, because the condition on line 416 was never false

417 li.append(chan) 

418 

419 if len(li) and not fullrescan: 419 ↛ 420line 419 didn't jump to line 420, because the condition on line 419 was never true

420 for c in li: 

421 if not refresh_scsi_channel(c): 

422 fullrescan = True 

423 

424 if fullrescan: 424 ↛ exitline 424 didn't return from function 'refresh_HostID', because the condition on line 424 was never false

425 util.SMlog("Full rescan of HostID %s" % HostID) 

426 path = '/sys/class/scsi_host/host%s/scan' % HostID 

427 if os.path.exists(path): 427 ↛ 428line 427 didn't jump to line 428, because the condition on line 427 was never true

428 try: 

429 scanstring = "- - -" 

430 f = open(path, 'w') 

431 f.write('%s\n' % scanstring) 

432 f.close() 

433 if len(li): 

434 # Channels already exist, allow some time for 

435 # undiscovered LUNs/channels to appear 

436 time.sleep(2) 

437 except: 

438 pass 

439 # Host Bus scan issued, now try to detect channels 

440 if util.wait_for_path("/sys/class/scsi_disk/%s*" % HostID, 5): 440 ↛ exitline 440 didn't return from function 'refresh_HostID', because the condition on line 440 was never false

441 # At least one LUN is mapped 

442 LUNs = glob.glob('/sys/class/scsi_disk/%s*' % HostID) 

443 li = [] 

444 for l in LUNs: 

445 chan = re.sub(":[0-9]*$", '', os.path.basename(l)) 

446 if chan not in li: 446 ↛ 444line 446 didn't jump to line 444, because the condition on line 446 was never false

447 li.append(chan) 

448 for c in li: 

449 refresh_scsi_channel(c) 

450 

451 

452def refresh_scsi_channel(channel): 

453 DEV_WAIT = 5 

454 util.SMlog("Refreshing channel %s" % channel) 

455 util.wait_for_path('/dev/disk/by-scsibus/*-%s*' % channel, DEV_WAIT) 

456 LUNs = glob.glob('/dev/disk/by-scsibus/*-%s*' % channel) 

457 try: 

458 rootdevs = util.dom0_disks() 

459 except: 

460 util.SMlog("Failed to query root disk, failing operation") 

461 return False 

462 

463 # a) Find a LUN to issue a Query LUNs command 

464 li = [] 

465 Query = False 

466 for lun in LUNs: 

467 try: 

468 hbtl = lun.split('-')[-1] 

469 h = hbtl.split(':') 

470 l = util.pread2(["/usr/bin/sg_luns", "-q", lun]).split('\n') 

471 li = [] 

472 for i in l: 

473 if len(i): 

474 li.append(int(i[0:4], 16)) 

475 util.SMlog("sg_luns query returned %s" % li) 

476 Query = True 

477 break 

478 except: 

479 pass 

480 if not Query: 

481 util.SMlog("Failed to detect or query LUN on Channel %s" % channel) 

482 return False 

483 

484 # b) Remove stale LUNs 

485 current = glob.glob('/dev/disk/by-scsibus/*-%s:%s:%s*' % (h[0], h[1], h[2])) 

486 for cur in current: 

487 lunID = int(cur.split(':')[-1]) 

488 newhbtl = ['', h[0], h[1], h[2], str(lunID)] 

489 if os.path.realpath(cur) in rootdevs: 

490 # Don't touch the rootdev 

491 if lunID in li: 

492 li.remove(lunID) 

493 continue 

494 

495 # Check if LUN is stale, and remove it 

496 if not lunID in li: 

497 util.SMlog("Stale LUN detected. Removing HBTL: %s" % newhbtl) 

498 scsi_dev_ctrl(newhbtl, "remove") 

499 util.wait_for_nopath(cur, DEV_WAIT) 

500 continue 

501 else: 

502 li.remove(lunID) 

503 

504 # Check if the device is still present 

505 if not os.path.exists(cur): 

506 continue 

507 

508 # Query SCSIid, check it matches, if not, re-probe 

509 cur_SCSIid = os.path.basename(cur).split("-%s:%s:%s" % (h[0], h[1], h[2]))[0] 

510 real_SCSIid = getSCSIid(cur) 

511 if cur_SCSIid != real_SCSIid: 

512 util.SMlog("HBTL %s does not match, re-probing" % newhbtl) 

513 scsi_dev_ctrl(newhbtl, "remove") 

514 util.wait_for_nopath(cur, DEV_WAIT) 

515 scsi_dev_ctrl(newhbtl, "add") 

516 util.wait_for_path('/dev/disk/by-scsibus/%s-%s' % (real_SCSIid, hbtl), DEV_WAIT) 

517 pass 

518 

519 # c) Probe for any LUNs that are not present in the system 

520 for l in li: 

521 newhbtl = ['', h[0], h[1], h[2], str(l)] 

522 newhbtlstr = "%s:%s:%s:%s" % (h[0], h[1], h[2], str(l)) 

523 util.SMlog("Probing new HBTL: %s" % newhbtl) 

524 scsi_dev_ctrl(newhbtl, "add") 

525 util.wait_for_path('/dev/disk/by-scsibus/*-%s' % newhbtlstr, DEV_WAIT) 

526 

527 return True 

528 

529 

530def refreshdev(pathlist): 

531 """ 

532 Refresh block devices given a path list 

533 """ 

534 for path in pathlist: 

535 dev = getdev(path) 

536 sysfs = os.path.join('/sys/block', dev, 'device/rescan') 

537 if os.path.exists(sysfs): 

538 try: 

539 with open(sysfs, 'w') as f: 

540 f.write('1') 

541 except: 

542 pass 

543 

544 

545def refresh_lun_size_by_SCSIid(SCSIid): 

546 """ 

547 Refresh all devices for the SCSIid. 

548 Returns True if all known devices and the mapper device are up to date. 

549 """ 

550 

551 def get_primary_device(SCSIid): 

552 mapperdevice = os.path.join('/dev/mapper', SCSIid) 

553 if os.path.exists(mapperdevice): 

554 return mapperdevice 

555 else: 

556 devices = get_devices_by_SCSIid(SCSIid) 

557 if devices: 

558 return devices[0] 

559 else: 

560 return None 

561 

562 def get_outdated_size_devices(currentcapacity, devices): 

563 devicesthatneedrefresh = [] 

564 for device in devices: 

565 if getsize(device) != currentcapacity: 

566 devicesthatneedrefresh.append(device) 

567 return devicesthatneedrefresh 

568 

569 def refresh_devices_if_needed(primarydevice, SCSIid, currentcapacity): 

570 devices = get_devices_by_SCSIid(SCSIid) 

571 if "/dev/mapper/" in primarydevice: 

572 devices = set(devices + mpath_cli.list_paths(SCSIid)) 

573 devicesthatneedrefresh = get_outdated_size_devices(currentcapacity, 

574 devices) 

575 if devicesthatneedrefresh: 

576 # timing out avoids waiting for min(dev_loss_tmo, fast_io_fail_tmo) 

577 # if one or multiple devices don't answer 

578 util.timeout_call(10, refreshdev, devicesthatneedrefresh) 

579 if get_outdated_size_devices(currentcapacity, 

580 devicesthatneedrefresh): 

581 # in this state we shouldn't force resizing the mapper dev 

582 raise util.SMException("Failed to get %s to agree on the " 

583 "current capacity." 

584 % devicesthatneedrefresh) 

585 

586 def refresh_mapper_if_needed(primarydevice, SCSIid, currentcapacity): 

587 if "/dev/mapper/" in primarydevice \ 

588 and get_outdated_size_devices(currentcapacity, [primarydevice]): 

589 mpath_cli.resize_map(SCSIid) 

590 if get_outdated_size_devices(currentcapacity, [primarydevice]): 

591 raise util.SMException("Failed to get the mapper dev to agree " 

592 "on the current capacity.") 

593 

594 try: 

595 primarydevice = get_primary_device(SCSIid) 

596 if primarydevice: 

597 currentcapacity = sg_readcap(primarydevice) 

598 refresh_devices_if_needed(primarydevice, SCSIid, currentcapacity) 

599 refresh_mapper_if_needed(primarydevice, SCSIid, currentcapacity) 

600 else: 

601 util.SMlog("scsiutil.refresh_lun_size_by_SCSIid(%s) could not " 

602 "find any devices for the SCSIid." % SCSIid) 

603 return True 

604 except: 

605 util.logException(f"Error in scsiutil.refresh_lun_size_by_SCSIid({SCSIid}: {traceback.format_exc()})") 

606 return False 

607 

608 

609def refresh_lun_size_by_SCSIid_on_slaves(session, SCSIid): 

610 for slave in util.get_all_slaves(session): 

611 util.SMlog("Calling on-slave.refresh_lun_size_by_SCSIid(%s) on %s." 

612 % (SCSIid, slave)) 

613 resulttext = session.xenapi.host.call_plugin( 

614 slave, 

615 "on-slave", 

616 "refresh_lun_size_by_SCSIid", 

617 {'SCSIid': SCSIid}) 

618 if "True" == resulttext: 

619 util.SMlog("Calling on-slave.refresh_lun_size_by_SCSIid(%s) on" 

620 " %s succeeded." % (SCSIid, slave)) 

621 else: 

622 message = ("Failed in on-slave.refresh_lun_size_by_SCSIid(%s) " 

623 "on %s." % (SCSIid, slave)) 

624 raise util.SMException("Slave %s failed in on-slave.refresh_lun_" 

625 "size_by_SCSIid(%s) " % (slave, SCSIid)) 

626 

627 

628def remove_stale_luns(hostids, lunid, expectedPath, mpath): 

629 try: 

630 for hostid in hostids: 

631 # get all LUNs of the format hostid:x:y:lunid 

632 luns = glob.glob('/dev/disk/by-scsibus/*-%s:*:*:%s' % (hostid, lunid)) 

633 

634 # try to get the scsiid for each of these luns 

635 for lun in luns: 

636 try: 

637 getSCSIid(lun) 

638 # if it works, we have a problem as this device should not 

639 # be present and be valid on this system 

640 util.SMlog("Warning! The lun %s should not be present and" \ 

641 " be valid on this system." % lun) 

642 except: 

643 # Now do the rest. 

644 pass 

645 

646 # get the HBTL 

647 basename = os.path.basename(lun) 

648 hbtl_list = basename.split(':') 

649 hbtl = basename.split('-')[1] 

650 

651 # the first one in scsiid-hostid 

652 hbtl_list[0] = hbtl_list[0].split('-')[1] 

653 

654 expectedPath = expectedPath + '*' + hbtl 

655 if not os.path.exists(expectedPath): 

656 # wait for sometime and check if the expected path exists 

657 # check if a rescan was done outside of this process 

658 time.sleep(2) 

659 

660 if os.path.exists(expectedPath): 

661 # do not remove device, this might be dangerous 

662 util.SMlog("Path %s appeared before checking for " \ 

663 "stale LUNs, ignore this LUN %s." % (expectedPath, lun)) 

664 continue 

665 

666 # remove the scsi device 

667 l = [os.path.realpath(lun), hbtl_list[0], hbtl_list[1], \ 

668 hbtl_list[2], hbtl_list[3]] 

669 scsi_dev_ctrl(l, 'remove') 

670 

671 # if multipath is enabled, do a best effort cleanup 

672 if mpath: 

673 try: 

674 path = os.path.basename(os.path.realpath(lun)) 

675 mpath_cli.remove_path(path) 

676 except Exception as e: 

677 util.SMlog("Failed to remove path %s, ignoring " \ 

678 "exception as path may not be present." % path) 

679 except Exception as e: 

680 util.SMlog("Exception removing stale LUNs, new devices may not come" \ 

681 " up properly! Error: %s" % str(e)) 

682 

683 

684def sg_readcap(device): 

685 device = os.path.join('/dev', getdev(device)) 

686 readcapcommand = ['/usr/bin/sg_readcap', '-b', device] 

687 (rc, stdout, stderr) = util.doexec(readcapcommand) 

688 if rc == 6: 

689 # retry one time for "Capacity data has changed" 

690 (rc, stdout, stderr) = util.doexec(readcapcommand) 

691 if rc != 0: 691 ↛ 692line 691 didn't jump to line 692, because the condition on line 691 was never true

692 raise util.SMException("scsiutil.sg_readcap(%s) failed" % (device)) 

693 match = re.search('(^|.*\n)(0x[0-9a-fA-F]+) (0x[0-9a-fA-F]+)\n$', stdout) 

694 if not match: 694 ↛ 695line 694 didn't jump to line 695, because the condition on line 694 was never true

695 raise util.SMException("scsiutil.sg_readcap(%s) failed to parse: %s" 

696 % (device, stdout)) 

697 (blockcount, blocksize) = match.group(2, 3) 

698 return (int(blockcount, 0) * int(blocksize, 0))