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# ISCSISR: ISCSI software initiator SR driver 

19# 

20 

21import SR 

22import util 

23import time 

24import LUNperVDI 

25import os 

26import sys 

27import re 

28import glob 

29import xml.dom.minidom 

30import scsiutil 

31import iscsilib 

32import xs_errors 

33 

34INITIATORNAME_FILE = '/etc/iscsi/initiatorname.iscsi' 

35SECTOR_SHIFT = 9 

36DEFAULT_PORT = 3260 

37# 2^16 Max port number value 

38MAXPORT = 65535 

39MAX_TIMEOUT = 15 

40MAX_LUNID_TIMEOUT = 60 

41ISCSI_PROCNAME = "iscsi_tcp" 

42 

43 

44class BaseISCSISR(SR.SR): 

45 """ISCSI storage repository""" 

46 

47 @property 

48 def force_tapdisk(self): 

49 return self.dconf.get('force_tapdisk', 'false') == 'true' 

50 

51 @property 

52 def attached(self): 

53 if not self._attached: 53 ↛ 54line 53 didn't jump to line 54, because the condition on line 53 was never true

54 self._attached = False 

55 self._attached = iscsilib._checkTGT(self.targetIQN) 

56 return self._attached 

57 

58 @attached.setter 

59 def attached(self, value): 

60 self._attached = value 

61 

62 @property 

63 def pathdict(self): 

64 if not self._pathdict: 64 ↛ 65line 64 didn't jump to line 65, because the condition on line 64 was never true

65 self._initPaths() 

66 return self._pathdict 

67 

68 @property 

69 def adapter(self): 

70 if not self._adapter: 70 ↛ 71line 70 didn't jump to line 71, because the condition on line 70 was never true

71 self._initPaths() 

72 return self._adapter 

73 

74 @adapter.setter 

75 def adapter(self, value): 

76 self._adapter = value 

77 

78 @property 

79 def devs(self): 

80 if not self._devs: 

81 self._initPaths() 

82 return self._devs 

83 

84 @property 

85 def tgtidx(self): 

86 """This appears to only be referenced by a unit test. Do we really need it? """ 

87 if not self._tgtidx: 87 ↛ 88line 87 didn't jump to line 88, because the condition on line 87 was never true

88 self._initPaths() 

89 return self._tgtidx 

90 

91 @property 

92 def path(self): 

93 if not self._path: 93 ↛ 94line 93 didn't jump to line 94, because the condition on line 93 was never true

94 self._initPaths() 

95 return self._path 

96 

97 @property 

98 def address(self): 

99 if not self._address: 99 ↛ 100line 99 didn't jump to line 100, because the condition on line 99 was never true

100 self._initPaths() 

101 return self._address 

102 

103 def handles(type): 

104 return False 

105 handles = staticmethod(handles) 

106 

107 def _synchroniseAddrList(self, addrlist): 

108 if not self.multihomed: 

109 return 

110 change = False 

111 if 'multihomelist' not in self.dconf: 

112 change = True 

113 self.mlist = [] 

114 mstr = "" 

115 else: 

116 self.mlist = self.dconf['multihomelist'].split(',') 

117 mstr = self.dconf['multihomelist'] 

118 for val in addrlist: 

119 if not val in self.mlist: 

120 self.mlist.append(val) 

121 if len(mstr): 

122 mstr += "," 

123 mstr += val 

124 change = True 

125 if change: 

126 pbd = None 

127 try: 

128 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

129 if pbd is not None: 

130 device_config = self.session.xenapi.PBD.get_device_config(pbd) 

131 device_config['multihomelist'] = mstr 

132 self.session.xenapi.PBD.set_device_config(pbd, device_config) 

133 except: 

134 pass 

135 

136 def load(self, sr_uuid): 

137 if self.force_tapdisk: 

138 self.sr_vditype = 'aio' 

139 else: 

140 self.sr_vditype = 'phy' 

141 self.discoverentry = 0 

142 self.default_vdi_visibility = False 

143 

144 # Required parameters 

145 if 'target' not in self.dconf or not self.dconf['target']: 145 ↛ 146line 145 didn't jump to line 146, because the condition on line 145 was never true

146 raise xs_errors.XenError('ConfigTargetMissing') 

147 

148 # we are no longer putting hconf in the xml. 

149 # Instead we pass a session and host ref and let the SM backend query XAPI itself 

150 try: 

151 if 'localIQN' not in self.dconf: 151 ↛ 152line 151 didn't jump to line 152, because the condition on line 151 was never true

152 self.localIQN = self.session.xenapi.host.get_other_config(self.host_ref)['iscsi_iqn'] 

153 else: 

154 self.localIQN = self.dconf['localIQN'] 

155 except: 

156 raise xs_errors.XenError('ConfigISCSIIQNMissing') 

157 

158 # Check for empty string 

159 if not self.localIQN: 159 ↛ 160line 159 didn't jump to line 160, because the condition on line 159 was never true

160 raise xs_errors.XenError('ConfigISCSIIQNMissing') 

161 

162 try: 

163 self.target = util._convertDNS(self.dconf['target'].split(',')[0]) 

164 except: 

165 raise xs_errors.XenError('DNSError') 

166 

167 self.targetlist = self.target 

168 if 'targetlist' in self.dconf: 168 ↛ 169line 168 didn't jump to line 169, because the condition on line 168 was never true

169 self.targetlist = self.dconf['targetlist'] 

170 

171 # Optional parameters 

172 self.chapuser = "" 

173 self.chappassword = "" 

174 if 'chapuser' in self.dconf \ 

175 and ('chappassword' in self.dconf or 'chappassword_secret' in self.dconf): 

176 self.chapuser = self.dconf['chapuser'].encode('utf-8') 

177 if 'chappassword_secret' in self.dconf: 177 ↛ 178line 177 didn't jump to line 178, because the condition on line 177 was never true

178 self.chappassword = util.get_secret(self.session, self.dconf['chappassword_secret']) 

179 else: 

180 self.chappassword = self.dconf['chappassword'] 

181 

182 self.chappassword = self.chappassword.encode('utf-8') 

183 

184 self.incoming_chapuser = "" 

185 self.incoming_chappassword = "" 

186 if 'incoming_chapuser' in self.dconf \ 

187 and ('incoming_chappassword' in self.dconf or 'incoming_chappassword_secret' in self.dconf): 

188 self.incoming_chapuser = self.dconf['incoming_chapuser'].encode('utf-8') 

189 if 'incoming_chappassword_secret' in self.dconf: 189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true

190 self.incoming_chappassword = util.get_secret(self.session, self.dconf['incoming_chappassword_secret']) 

191 else: 

192 self.incoming_chappassword = self.dconf['incoming_chappassword'] 

193 

194 self.incoming_chappassword = self.incoming_chappassword.encode('utf-8') 

195 

196 self.port = DEFAULT_PORT 

197 if 'port' in self.dconf and self.dconf['port']: 197 ↛ 198line 197 didn't jump to line 198, because the condition on line 197 was never true

198 try: 

199 self.port = int(self.dconf['port']) 

200 except: 

201 raise xs_errors.XenError('ISCSIPort') 

202 if self.port > MAXPORT or self.port < 1: 202 ↛ 203line 202 didn't jump to line 203, because the condition on line 202 was never true

203 raise xs_errors.XenError('ISCSIPort') 

204 

205 # For backwards compatibility 

206 if 'usediscoverynumber' in self.dconf: 206 ↛ 207line 206 didn't jump to line 207, because the condition on line 206 was never true

207 self.discoverentry = self.dconf['usediscoverynumber'] 

208 

209 self.multihomed = False 

210 if 'multihomed' in self.dconf: 210 ↛ 211line 210 didn't jump to line 211, because the condition on line 210 was never true

211 if self.dconf['multihomed'] == "true": 

212 self.multihomed = True 

213 elif self.mpath == 'true': 213 ↛ 214line 213 didn't jump to line 214, because the condition on line 213 was never true

214 self.multihomed = True 

215 

216 if 'targetIQN' not in self.dconf or not self.dconf['targetIQN']: 216 ↛ 217line 216 didn't jump to line 217, because the condition on line 216 was never true

217 self._scan_IQNs() 

218 raise xs_errors.XenError('ConfigTargetIQNMissing') 

219 

220 self.targetIQN = self.dconf['targetIQN'] 

221 

222 self._attached = None 

223 self._pathdict = None 

224 self._adapter = None 

225 self._devs = None 

226 self._tgtidx = None 

227 self._path = None 

228 self._address = None 

229 

230 def _initPaths(self): 

231 self._init_adapters() 

232 # Generate a list of all possible paths 

233 self._pathdict = {} 

234 addrlist = [] 

235 rec = {} 

236 key = "%s:%d" % (self.target, self.port) 

237 rec['ipaddr'] = self.target 

238 rec['port'] = self.port 

239 rec['path'] = os.path.join("/dev/iscsi", self.targetIQN, \ 

240 key) 

241 self._pathdict[key] = rec 

242 util.SMlog("PATHDICT: key %s: %s" % (key, rec)) 

243 self._tgtidx = key 

244 addrlist.append(key) 

245 

246 self._path = rec['path'] 

247 self._address = self.tgtidx 

248 if not self.attached: 248 ↛ 249line 248 didn't jump to line 249, because the condition on line 248 was never true

249 return 

250 

251 if self.multihomed: 251 ↛ 267line 251 didn't jump to line 267, because the condition on line 251 was never false

252 map = iscsilib.get_node_records(targetIQN=self.targetIQN) 

253 for i in range(0, len(map)): 

254 (portal, tpgt, iqn) = map[i] 

255 (ipaddr, port) = iscsilib.parse_IP_port(portal) 

256 if self.target != ipaddr: 256 ↛ 253line 256 didn't jump to line 253, because the condition on line 256 was never false

257 key = "%s:%s" % (ipaddr, port) 

258 rec = {} 

259 rec['ipaddr'] = ipaddr 

260 rec['port'] = int(port) 

261 rec['path'] = os.path.join("/dev/iscsi", self.targetIQN, \ 

262 key) 

263 self._pathdict[key] = rec 

264 util.SMlog("PATHDICT: key %s: %s" % (key, rec)) 

265 addrlist.append(key) 

266 

267 if not os.path.exists(self.path): 

268 # Try to detect an active path in order of priority 

269 for key in self.pathdict: 269 ↛ 276line 269 didn't jump to line 276, because the loop on line 269 didn't complete

270 if key in self.adapter: 

271 self._tgtidx = key 

272 self._path = self.pathdict[self.tgtidx]['path'] 

273 if os.path.exists(self.path): 273 ↛ 269line 273 didn't jump to line 269, because the condition on line 273 was never false

274 util.SMlog("Path found: %s" % self.path) 

275 break 

276 self._address = self.tgtidx 

277 self._synchroniseAddrList(addrlist) 

278 

279 def _init_adapters(self): 

280 # Generate a list of active adapters 

281 ids = scsiutil._genHostList(ISCSI_PROCNAME) 

282 util.SMlog(ids) 

283 self._adapter = {} 

284 for host in ids: 

285 try: 

286 targetIQN = iscsilib.get_targetIQN(host) 

287 if targetIQN != self.targetIQN: 

288 continue 

289 (addr, port) = iscsilib.get_targetIP_and_port(host) 

290 entry = "%s:%s" % (addr, port) 

291 self._adapter[entry] = host 

292 except: 

293 pass 

294 self._devs = scsiutil.cacheSCSIidentifiers() 

295 

296 def attach(self, sr_uuid): 

297 self._mpathHandle() 

298 

299 multiTargets = False 

300 

301 npaths = 0 

302 try: 

303 pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

304 if pbdref: 

305 other_config = self.session.xenapi.PBD.get_other_config(pbdref) 

306 multiTargets = util.sessions_less_than_targets(other_config, self.dconf) 

307 except: 

308 pass 

309 

310 if not self.attached or multiTargets: 

311 # Verify iSCSI target and port 

312 if 'multihomelist' in self.dconf and 'multiSession' not in self.dconf: 

313 targetlist = self.dconf['multihomelist'].split(',') 

314 else: 

315 targetlist = ['%s:%d' % (self.target, self.port)] 

316 conn = False 

317 for val in targetlist: 

318 (target, port) = iscsilib.parse_IP_port(val) 

319 try: 

320 util._testHost(target, int(port), 'ISCSITarget') 

321 self.target = target 

322 self.port = int(port) 

323 conn = True 

324 break 

325 except: 

326 pass 

327 if not conn: 

328 raise xs_errors.XenError('ISCSITarget') 

329 

330 # Test and set the initiatorname file 

331 iscsilib.ensure_daemon_running_ok(self.localIQN) 

332 

333 # Check to see if auto attach was set 

334 if not iscsilib._checkTGT(self.targetIQN) or multiTargets: 

335 try: 

336 iqn_map = [] 

337 if 'any' != self.targetIQN: 

338 try: 

339 iqn_map = iscsilib.get_node_records(self.targetIQN) 

340 except: 

341 # Pass the exception that is thrown, when there 

342 # are no nodes 

343 pass 

344 if len(iqn_map) == 0: 

345 iqn_map = iscsilib.discovery(self.target, self.port, 

346 self.chapuser, self.chappassword, 

347 self.targetIQN, 

348 iscsilib.get_iscsi_interfaces()) 

349 if len(iqn_map) == 0: 

350 self._scan_IQNs() 

351 raise xs_errors.XenError('ISCSIDiscovery', 

352 opterr='check target settings') 

353 for i in range(0, len(iqn_map)): 

354 (portal, tpgt, iqn) = iqn_map[i] 

355 try: 

356 (ipaddr, port) = iscsilib.parse_IP_port(portal) 

357 if not self.multihomed and ipaddr != self.target: 

358 continue 

359 util._testHost(ipaddr, int(port), 'ISCSITarget') 

360 util.SMlog("Logging in to [%s:%s]" % (ipaddr, port)) 

361 iscsilib.login(portal, iqn, self.chapuser, 

362 self.chappassword, 

363 self.incoming_chapuser, 

364 self.incoming_chappassword, 

365 self.mpath == "true") 

366 npaths = npaths + 1 

367 except Exception as e: 

368 # Exceptions thrown in login are acknowledged, 

369 # the rest of exceptions are ignored since some of the 

370 # paths in multipath may not be reachable 

371 if str(e).startswith('ISCSI login'): 

372 raise 

373 else: 

374 pass 

375 

376 if not iscsilib._checkTGT(self.targetIQN): 

377 raise xs_errors.XenError('ISCSIDevice', \ 

378 opterr='during login') 

379 

380 # Allow the devices to settle 

381 time.sleep(5) 

382 

383 except util.CommandException as inst: 

384 raise xs_errors.XenError('ISCSILogin', \ 

385 opterr='code is %d' % inst.code) 

386 self.attached = True 

387 self._initPaths() 

388 util._incr_iscsiSR_refcount(self.targetIQN, sr_uuid) 

389 IQNs = [] 

390 if "multiSession" in self.dconf: 

391 IQNs = "" 

392 for iqn in self.dconf['multiSession'].split("|"): 

393 if len(iqn): 

394 IQNs += iqn.split(',')[2] 

395 else: 

396 IQNs.append(self.targetIQN) 

397 sessions = 0 

398 paths = iscsilib.get_IQN_paths() 

399 for path in paths: 

400 try: 

401 if util.get_single_entry(os.path.join(path, 'targetname')) in IQNs: 

402 sessions += 1 

403 util.SMlog("IQN match. Incrementing sessions to %d" % sessions) 

404 except: 

405 util.SMlog("Failed to read targetname path," \ 

406 + "iscsi_sessions value may be incorrect") 

407 

408 if pbdref: 

409 # Just to be safe in case of garbage left during crashes 

410 # we remove the key and add it 

411 self.session.xenapi.PBD.remove_from_other_config( 

412 pbdref, "iscsi_sessions") 

413 self.session.xenapi.PBD.add_to_other_config( 

414 pbdref, "iscsi_sessions", str(sessions)) 

415 

416 if 'SCSIid' in self.dconf: 

417 if self.mpath == 'true': 

418 self.mpathmodule.refresh(self.dconf['SCSIid'], 0) 

419 devs = os.listdir("/dev/disk/by-scsid/%s" % self.dconf['SCSIid']) 

420 for dev in devs: 

421 realdev = os.path.realpath("/dev/disk/by-scsid/%s/%s" % (self.dconf['SCSIid'], dev)) 

422 util.set_scheduler(realdev.split("/")[-1], "noop") 

423 

424 def detach(self, sr_uuid, delete=False): 

425 keys = [] 

426 pbdref = None 

427 try: 

428 pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

429 except: 

430 pass 

431 if 'SCSIid' in self.dconf: 

432 self.mpathmodule.reset(self.dconf['SCSIid'], explicit_unmap=True) 

433 keys.append("mpath-" + self.dconf['SCSIid']) 

434 

435 # Remove iscsi_sessions and multipathed keys 

436 if pbdref is not None: 

437 if self.cmd == 'sr_detach': 

438 keys += ["multipathed", "iscsi_sessions", "MPPEnabled"] 

439 for key in keys: 

440 try: 

441 self.session.xenapi.PBD.remove_from_other_config(pbdref, key) 

442 except: 

443 pass 

444 

445 if util._decr_iscsiSR_refcount(self.targetIQN, sr_uuid) != 0: 

446 return 

447 

448 if self.direct and util._containsVDIinuse(self): 

449 return 

450 

451 if iscsilib._checkTGT(self.targetIQN): 

452 try: 

453 iscsilib.logout(self.target, self.targetIQN, all=True) 

454 if delete: 

455 iscsilib.delete(self.targetIQN) 

456 except util.CommandException as inst: 

457 raise xs_errors.XenError('ISCSIQueryDaemon', \ 

458 opterr='error is %d' % inst.code) 

459 if iscsilib._checkTGT(self.targetIQN): 

460 raise xs_errors.XenError('ISCSIQueryDaemon', \ 

461 opterr='Failed to logout from target') 

462 

463 self.attached = False 

464 

465 def create(self, sr_uuid, size): 

466 # Check whether an SR already exists 

467 SRs = self.session.xenapi.SR.get_all_records() 

468 for sr in SRs: 

469 record = SRs[sr] 

470 sm_config = record["sm_config"] 

471 if 'targetIQN' in sm_config and \ 

472 sm_config['targetIQN'] == self.targetIQN: 

473 raise xs_errors.XenError('SRInUse') 

474 self.attach(sr_uuid) 

475 # Wait up to MAX_TIMEOUT for devices to appear 

476 util.wait_for_path(self.path, MAX_TIMEOUT) 

477 

478 if self._loadvdis() > 0: 

479 scanrecord = SR.ScanRecord(self) 

480 scanrecord.synchronise() 

481 try: 

482 self.detach(sr_uuid) 

483 except: 

484 pass 

485 self.sm_config = self.session.xenapi.SR.get_sm_config(self.sr_ref) 

486 self.sm_config['disktype'] = 'Raw' 

487 self.sm_config['datatype'] = 'ISCSI' 

488 self.sm_config['target'] = self.target 

489 self.sm_config['targetIQN'] = self.targetIQN 

490 self.sm_config['multipathable'] = 'true' 

491 self.session.xenapi.SR.set_sm_config(self.sr_ref, self.sm_config) 

492 return 

493 

494 def delete(self, sr_uuid): 

495 self.detach(sr_uuid) 

496 return 

497 

498 def probe(self): 

499 SRs = self.session.xenapi.SR.get_all_records() 

500 Recs = {} 

501 for sr in SRs: 

502 record = SRs[sr] 

503 sm_config = record["sm_config"] 

504 if 'targetIQN' in sm_config and \ 

505 sm_config['targetIQN'] == self.targetIQN: 

506 Recs[record["uuid"]] = sm_config 

507 return self.srlist_toxml(Recs) 

508 

509 def scan(self, sr_uuid): 

510 if not self.passthrough: 

511 if not self.attached: 

512 raise xs_errors.XenError('SRUnavailable') 

513 self.refresh() 

514 time.sleep(2) # it seems impossible to tell when a scan's finished 

515 self._loadvdis() 

516 self.physical_utilisation = self.physical_size 

517 for uuid, vdi in self.vdis.items(): 

518 if vdi.managed: 

519 self.physical_utilisation += vdi.size 

520 self.virtual_allocation = self.physical_utilisation 

521 return super(BaseISCSISR, self).scan(sr_uuid) 

522 

523 def vdi(self, uuid): 

524 return LUNperVDI.RAWVDI(self, uuid) 

525 

526 def _scan_IQNs(self): 

527 # Verify iSCSI target and port 

528 util._testHost(self.target, self.port, 'ISCSITarget') 

529 

530 # Test and set the initiatorname file 

531 iscsilib.ensure_daemon_running_ok(self.localIQN) 

532 

533 map = iscsilib.discovery(self.target, self.port, self.chapuser, 

534 self.chappassword, 

535 interfaceArray=iscsilib.get_iscsi_interfaces()) 

536 map.append(("%s:%d" % (self.targetlist, self.port), "0", "*")) 

537 self.print_entries(map) 

538 

539 def _attach_LUN_bylunid(self, lunid): 

540 if not self.attached: 

541 raise xs_errors.XenError('SRUnavailable') 

542 connected = [] 

543 for val in self.adapter: 

544 if val not in self.pathdict: 

545 continue 

546 rec = self.pathdict[val] 

547 path = os.path.join(rec['path'], "LUN%s" % lunid) 

548 realpath = os.path.realpath(path) 

549 host = self.adapter[val] 

550 l = [realpath, host, 0, 0, lunid] 

551 

552 addDevice = True 

553 if realpath in self.devs: 

554 # if the device is stale remove it before adding again 

555 real_SCSIid = None 

556 try: 

557 real_SCSIid = scsiutil.getSCSIid(realpath) 

558 except: 

559 pass 

560 

561 if real_SCSIid is not None: 

562 # make sure this is the same scsiid, if not remove the device 

563 cur_scsibuspath = glob.glob('/dev/disk/by-scsibus/*-%s:0:0:%s' % (host, lunid)) 

564 cur_SCSIid = os.path.basename(cur_scsibuspath[0]).split("-")[0] 

565 if cur_SCSIid != real_SCSIid: 

566 # looks stale, remove it 

567 scsiutil.scsi_dev_ctrl(l, "remove") 

568 else: 

569 util.SMlog("Not attaching LUNID %s for adapter %s" \ 

570 " since the device exists and the scsi id %s seems" \ 

571 " to be valid. " % (lunid, val, real_SCSIid)) 

572 addDevice = False 

573 else: 

574 # looks stale, remove it 

575 scsiutil.scsi_dev_ctrl(l, "remove") 

576 

577 if addDevice: 

578 # add the device 

579 scsiutil.scsi_dev_ctrl(l, "add") 

580 if not util.wait_for_path(path, MAX_LUNID_TIMEOUT): 

581 util.SMlog("Unable to detect LUN attached to host on path [%s]" % path) 

582 continue 

583 connected.append(path) 

584 return connected 

585 

586 def _attach_LUN_byserialid(self, serialid): 

587 if not self.attached: 

588 raise xs_errors.XenError('SRUnavailable') 

589 connected = [] 

590 for val in self.adapter: 

591 if val not in self.pathdict: 

592 continue 

593 rec = self.pathdict[val] 

594 path = os.path.join(rec['path'], "SERIAL-%s" % serialid) 

595 realpath = os.path.realpath(path) 

596 if realpath not in self.devs: 

597 if not util.wait_for_path(path, 5): 

598 util.SMlog("Unable to detect LUN attached to host on serial path [%s]" % path) 

599 continue 

600 connected.append(path) 

601 return connected 

602 

603 def _detach_LUN_bylunid(self, lunid, SCSIid): 

604 if not self.attached: 

605 raise xs_errors.XenError('SRUnavailable') 

606 if self.mpath == 'true' and len(SCSIid): 

607 self.mpathmodule.reset(SCSIid, explicit_unmap=True) 

608 util.remove_mpathcount_field(self.session, self.host_ref, self.sr_ref, SCSIid) 

609 for val in self.adapter: 

610 if val not in self.pathdict: 

611 continue 

612 rec = self.pathdict[val] 

613 path = os.path.join(rec['path'], "LUN%s" % lunid) 

614 realpath = os.path.realpath(path) 

615 if realpath in self.devs: 

616 util.SMlog("Found key: %s" % realpath) 

617 scsiutil.scsi_dev_ctrl(self.devs[realpath], 'remove') 

618 # Wait for device to disappear 

619 if not util.wait_for_nopath(realpath, MAX_LUNID_TIMEOUT): 

620 util.SMlog("Device has not disappeared after %d seconds" % \ 

621 MAX_LUNID_TIMEOUT) 

622 else: 

623 util.SMlog("Device [%s,%s] disappeared" % (realpath, path)) 

624 

625 def _attach_LUN_bySCSIid(self, SCSIid): 

626 if not self.attached: 

627 raise xs_errors.XenError('SRUnavailable') 

628 

629 path = self.mpathmodule.path(SCSIid) 

630 if not util.pathexists(path): 

631 self.refresh() 

632 if not util.wait_for_path(path, MAX_TIMEOUT): 

633 util.SMlog("Unable to detect LUN attached to host [%s]" \ 

634 % path) 

635 return False 

636 return True 

637 

638 # This function queries the session for the attached LUNs 

639 def _loadvdis(self): 

640 count = 0 

641 if not os.path.exists(self.path): 

642 return 0 

643 for file in filter(self.match_lun, util.listdir(self.path)): 

644 vdi_path = os.path.join(self.path, file) 

645 LUNid = file.replace("LUN", "") 

646 uuid = scsiutil.gen_uuid_from_string(scsiutil.getuniqueserial(vdi_path)) 

647 obj = self.vdi(uuid) 

648 obj._query(vdi_path, LUNid) 

649 self.vdis[uuid] = obj 

650 self.physical_size += obj.size 

651 count += 1 

652 return count 

653 

654 def refresh(self): 

655 for val in self.adapter: 

656 util.SMlog("Rescanning host adapter %s" % self.adapter[val]) 

657 scsiutil.rescan([self.adapter[val]]) 

658 

659 # Helper function for LUN-per-VDI VDI.introduce 

660 def _getLUNbySMconfig(self, sm_config): 

661 if 'LUNid' not in sm_config: 

662 raise xs_errors.XenError('VDIUnavailable') 

663 LUNid = int(sm_config['LUNid']) 

664 if not len(self._attach_LUN_bylunid(LUNid)): 

665 raise xs_errors.XenError('VDIUnavailable') 

666 return os.path.join(self.path, "LUN%d" % LUNid) 

667 

668 # This function takes an ISCSI device and populate it with 

669 # a dictionary of available LUNs on that target. 

670 def print_LUNs(self): 

671 self.LUNs = {} 

672 if os.path.exists(self.path): 

673 dom0_disks = util.dom0_disks() 

674 for file in util.listdir(self.path): 

675 if file.find("LUN") != -1 and file.find("_") == -1: 

676 vdi_path = os.path.join(self.path, file) 

677 if os.path.realpath(vdi_path) in dom0_disks: 

678 util.SMlog("Hide dom0 boot disk LUN") 

679 else: 

680 LUNid = file.replace("LUN", "") 

681 obj = self.vdi(self.uuid) 

682 obj._query(vdi_path, LUNid) 

683 self.LUNs[obj.uuid] = obj 

684 

685 def print_entries(self, map): 

686 dom = xml.dom.minidom.Document() 

687 element = dom.createElement("iscsi-target-iqns") 

688 dom.appendChild(element) 

689 count = 0 

690 for address, tpgt, iqn in map: 

691 entry = dom.createElement('TGT') 

692 element.appendChild(entry) 

693 subentry = dom.createElement('Index') 

694 entry.appendChild(subentry) 

695 textnode = dom.createTextNode(str(count)) 

696 subentry.appendChild(textnode) 

697 

698 try: 

699 # We always expect a port so this holds 

700 # regardless of IP version 

701 (addr, port) = address.rsplit(':', 1) 

702 except: 

703 addr = address 

704 port = DEFAULT_PORT 

705 subentry = dom.createElement('IPAddress') 

706 entry.appendChild(subentry) 

707 textnode = dom.createTextNode(str(addr)) 

708 subentry.appendChild(textnode) 

709 

710 if int(port) != DEFAULT_PORT: 

711 subentry = dom.createElement('Port') 

712 entry.appendChild(subentry) 

713 textnode = dom.createTextNode(str(port)) 

714 subentry.appendChild(textnode) 

715 

716 subentry = dom.createElement('TargetIQN') 

717 entry.appendChild(subentry) 

718 textnode = dom.createTextNode(str(iqn)) 

719 subentry.appendChild(textnode) 

720 count += 1 

721 print(dom.toprettyxml(), file=sys.stderr) 

722 

723 def srlist_toxml(self, SRs): 

724 dom = xml.dom.minidom.Document() 

725 element = dom.createElement("SRlist") 

726 dom.appendChild(element) 

727 

728 for val in SRs: 

729 record = SRs[val] 

730 entry = dom.createElement('SR') 

731 element.appendChild(entry) 

732 

733 subentry = dom.createElement("UUID") 

734 entry.appendChild(subentry) 

735 textnode = dom.createTextNode(val) 

736 subentry.appendChild(textnode) 

737 

738 subentry = dom.createElement("Target") 

739 entry.appendChild(subentry) 

740 textnode = dom.createTextNode(record['target']) 

741 subentry.appendChild(textnode) 

742 

743 subentry = dom.createElement("TargetIQN") 

744 entry.appendChild(subentry) 

745 textnode = dom.createTextNode(record['targetIQN']) 

746 subentry.appendChild(textnode) 

747 return dom.toprettyxml() 

748 

749 def match_lun(self, s): 

750 regex = re.compile("_") 

751 if regex.search(s, 0): 

752 return False 

753 regex = re.compile("LUN") 

754 return regex.search(s, 0)