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

213 Return the canonical device path(s) for a given SCSI ID 

214 Args: 

215 SCSIid: The SCSI ID of the devices to return 

216 

217 Returns: 

218 List of canonicalised devices 

219 """ 

220 scsid_path = os.path.join('/dev/disk/by-scsid', SCSIid) 

221 devices = os.listdir(scsid_path) 

222 if 'mapper' in devices: 222 ↛ 223line 222 didn't jump to line 223, because the condition on line 222 was never true

223 devices.remove('mapper') 

224 return [os.path.realpath(os.path.join(scsid_path, d)) for 

225 d in devices] 

226 

227 

228def rawdev(dev): 

229 device = getdev(dev) 

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

231 return device 

232 

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

234 

235 

236def getSessionID(path): 

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

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

239 

240 

241def match_session(s): 

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

243 return regex.search(s, 0) 

244 

245 

246def match_vendor(s): 

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

248 return regex.search(s, 0) 

249 

250 

251def match_dm(s): 

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

253 return regex.search(s, 0) 

254 

255 

256def match_sd(s): 

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

258 return regex.search(s, 0) 

259 

260 

261def _isSCSIdev(dev): 

262 if match_dm(dev): 

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

264 else: 

265 path = dev 

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

267 

268 

269def add_serial_record(session, sr_ref, devstring): 

270 try: 

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

272 conf['devserial'] = devstring 

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

274 except: 

275 pass 

276 

277 

278def get_serial_record(session, sr_ref): 

279 try: 

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

281 return conf['devserial'] 

282 except: 

283 return "" 

284 

285 

286def devlist_to_serialstring(devlist): 

287 serial = '' 

288 for dev in devlist: 

289 try: 

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

291 if not len(devserial) > 0: 

292 continue 

293 if len(serial): 

294 serial += ',' 

295 serial += devserial 

296 except: 

297 pass 

298 

299 return serial 

300 

301 

302def gen_synthetic_page_data(uuid): 

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

304 # we set the vendor ID to XENSRC 

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

306 # to 16 characters 

307 page80 = b'' 

308 page80 += b'\x00\x80' 

309 page80 += b'\x00\x12' 

310 page80 += str.encode(uuid[0:16]) 

311 page80 += str.encode(" ") 

312 

313 page83 = b'' 

314 page83 += b'\x00\x83' 

315 page83 += b'\x00\x31' 

316 page83 += b'\x02\x01\x00\x2d' 

317 page83 += str.encode("XENSRC ") 

318 page83 += str.encode(uuid) 

319 page83 += str.encode(" ") 

320 return ["", base64.b64encode(page80).decode(), 

321 base64.b64encode(page83).decode()] 

322 

323 

324def gen_raw_page_data(path): 

325 default = "" 

326 page80 = "" 

327 page83 = "" 

328 try: 

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

330 text = util.pread2(cmd) 

331 default = base64.b64encode(text) 

332 

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

334 text = util.pread2(cmd) 

335 page80 = base64.b64encode(text) 

336 

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

338 text = util.pread2(cmd) 

339 page83 = base64.b64encode(text) 

340 except: 

341 pass 

342 return [default, page80, page83] 

343 

344 

345def update_XS_SCSIdata(vdi_uuid, data): 

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

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

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

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

350 # the original array, fail and bluescreen. 

351 

352 xenstore_data = {} 

353 xenstore_data["vdi-uuid"] = vdi_uuid 

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

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

356 

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

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

359 

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

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

362 

363 return xenstore_data 

364 

365 

366def rescan(ids, fullrescan=True): 

367 for id in ids: 

368 refresh_HostID(id, fullrescan) 

369 

370 

371def _genHostList(procname): 

372 # loop through and check all adapters 

373 ids = [] 

374 try: 

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

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

377 if os.path.exists(filename): 

378 f = open(filename, 'r') 

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

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

381 f.close() 

382 except: 

383 pass 

384 return ids 

385 

386 

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

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

389 

390 devices = [] 

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

392 realpath = os.path.realpath(link) 

393 if os.path.exists(realpath): 

394 devices.append(realpath) 

395 return devices 

396 

397 

398def _genReverseSCSidtoLUNidmap(SCSIid): 

399 devices = [] 

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

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

402 return devices 

403 

404 

405def _dosgscan(): 

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

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

408 sgs = [] 

409 for line in scan: 

410 m = regex.match(line) 

411 if m: 

412 device = m.group(1) 

413 host = m.group(2) 

414 channel = m.group(3) 

415 sid = m.group(4) 

416 lun = m.group(5) 

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

418 return sgs 

419 

420 

421def refresh_HostID(HostID, fullrescan): 

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

423 li = [] 

424 for l in LUNs: 

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

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

427 li.append(chan) 

428 

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

430 for c in li: 

431 if not refresh_scsi_channel(c): 

432 fullrescan = True 

433 

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

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

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

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

438 try: 

439 scanstring = "- - -" 

440 f = open(path, 'w') 

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

442 f.close() 

443 if len(li): 

444 # Channels already exist, allow some time for 

445 # undiscovered LUNs/channels to appear 

446 time.sleep(2) 

447 except: 

448 pass 

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

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

451 # At least one LUN is mapped 

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

453 li = [] 

454 for l in LUNs: 

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

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

457 li.append(chan) 

458 for c in li: 

459 refresh_scsi_channel(c) 

460 

461 

462def refresh_scsi_channel(channel): 

463 DEV_WAIT = 5 

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

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

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

467 try: 

468 rootdevs = util.dom0_disks() 

469 except: 

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

471 return False 

472 

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

474 li = [] 

475 Query = False 

476 for lun in LUNs: 

477 try: 

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

479 h = hbtl.split(':') 

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

481 li = [] 

482 for i in l: 

483 if len(i): 

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

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

486 Query = True 

487 break 

488 except: 

489 pass 

490 if not Query: 

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

492 return False 

493 

494 # b) Remove stale LUNs 

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

496 for cur in current: 

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

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

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

500 # Don't touch the rootdev 

501 if lunID in li: 

502 li.remove(lunID) 

503 continue 

504 

505 # Check if LUN is stale, and remove it 

506 if not lunID in li: 

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

508 scsi_dev_ctrl(newhbtl, "remove") 

509 util.wait_for_nopath(cur, DEV_WAIT) 

510 continue 

511 else: 

512 li.remove(lunID) 

513 

514 # Check if the device is still present 

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

516 continue 

517 

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

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

520 real_SCSIid = getSCSIid(cur) 

521 if cur_SCSIid != real_SCSIid: 

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

523 scsi_dev_ctrl(newhbtl, "remove") 

524 util.wait_for_nopath(cur, DEV_WAIT) 

525 scsi_dev_ctrl(newhbtl, "add") 

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

527 pass 

528 

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

530 for l in li: 

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

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

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

534 scsi_dev_ctrl(newhbtl, "add") 

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

536 

537 return True 

538 

539 

540def refreshdev(pathlist): 

541 """ 

542 Refresh block devices given a path list 

543 """ 

544 for path in pathlist: 

545 dev = getdev(path) 

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

547 if os.path.exists(sysfs): 

548 try: 

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

550 f.write('1') 

551 except: 

552 pass 

553 

554 

555def refresh_lun_size_by_SCSIid(SCSIid): 

556 """ 

557 Refresh all devices for the SCSIid. 

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

559 """ 

560 

561 def get_primary_device(SCSIid): 

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

563 if os.path.exists(mapperdevice): 

564 return mapperdevice 

565 else: 

566 devices = get_devices_by_SCSIid(SCSIid) 

567 if devices: 

568 return devices[0] 

569 else: 

570 return None 

571 

572 def get_outdated_size_devices(currentcapacity, devices): 

573 devicesthatneedrefresh = [] 

574 for device in devices: 

575 if getsize(device) != currentcapacity: 

576 devicesthatneedrefresh.append(device) 

577 return devicesthatneedrefresh 

578 

579 def refresh_devices_if_needed(primarydevice, SCSIid, currentcapacity): 

580 devices = get_devices_by_SCSIid(SCSIid) 

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

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

583 devicesthatneedrefresh = get_outdated_size_devices(currentcapacity, 

584 devices) 

585 if devicesthatneedrefresh: 

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

587 # if one or multiple devices don't answer 

588 util.timeout_call(10, refreshdev, devicesthatneedrefresh) 

589 if get_outdated_size_devices(currentcapacity, 

590 devicesthatneedrefresh): 

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

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

593 "current capacity." 

594 % devicesthatneedrefresh) 

595 

596 if any([getsize(x) != sg_readcap(x) for x in devices]): 

597 raise util.SMException(f"Not all devices in {devices} agree on size and capacity") 

598 

599 def refresh_mapper_if_needed(primarydevice, SCSIid, currentcapacity): 

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

601 and get_outdated_size_devices(currentcapacity, [primarydevice]): 

602 mpath_cli.resize_map(SCSIid) 

603 if get_outdated_size_devices(currentcapacity, [primarydevice]): 

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

605 "on the current capacity.") 

606 

607 try: 

608 primarydevice = get_primary_device(SCSIid) 

609 if primarydevice: 

610 currentcapacity = sg_readcap(primarydevice) 

611 refresh_devices_if_needed(primarydevice, SCSIid, currentcapacity) 

612 refresh_mapper_if_needed(primarydevice, SCSIid, currentcapacity) 

613 else: 

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

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

616 return True 

617 except: 

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

619 return False 

620 

621 

622def refresh_lun_size_by_SCSIid_on_slaves(session, SCSIid): 

623 for slave in util.get_all_slaves(session): 

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

625 % (SCSIid, slave)) 

626 resulttext = session.xenapi.host.call_plugin( 

627 slave, 

628 "on-slave", 

629 "refresh_lun_size_by_SCSIid", 

630 {'SCSIid': SCSIid}) 

631 if "True" == resulttext: 

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

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

634 else: 

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

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

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

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

639 

640 

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

642 try: 

643 for hostid in hostids: 

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

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

646 

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

648 for lun in luns: 

649 try: 

650 getSCSIid(lun) 

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

652 # be present and be valid on this system 

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

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

655 except: 

656 # Now do the rest. 

657 pass 

658 

659 # get the HBTL 

660 basename = os.path.basename(lun) 

661 hbtl_list = basename.split(':') 

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

663 

664 # the first one in scsiid-hostid 

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

666 

667 expectedPath = expectedPath + '*' + hbtl 

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

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

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

671 time.sleep(2) 

672 

673 if os.path.exists(expectedPath): 

674 # do not remove device, this might be dangerous 

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

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

677 continue 

678 

679 # remove the scsi device 

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

681 hbtl_list[2], hbtl_list[3]] 

682 scsi_dev_ctrl(l, 'remove') 

683 

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

685 if mpath: 

686 try: 

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

688 mpath_cli.remove_path(path) 

689 except Exception as e: 

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

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

692 except Exception as e: 

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

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

695 

696 

697def sg_readcap(device): 

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

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

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

701 if rc == 6: 

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

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

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

705 util.SMlog(f"scsiutil.sg_readcap({device}) failed.\n{stdout}\n{stderr}") 

706 raise util.SMException(f"scsiutil.sg_readcap({device}) failed.") 

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

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

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

710 % (device, stdout)) 

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

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