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: 

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: 

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: 304 ↛ 310line 304 didn't jump to line 310, because the condition on line 304 was never false

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: 310 ↛ 312line 310 didn't jump to line 312, because the condition on line 310 was never true

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: 390 ↛ 391line 390 didn't jump to line 391, because the condition on line 390 was never true

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: 399 ↛ 400line 399 didn't jump to line 400, because the loop on line 399 never started

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: 408 ↛ 416line 408 didn't jump to line 416, because the condition on line 408 was never false

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: 416 ↛ exitline 416 didn't return from function 'attach', because the condition on line 416 was never false

417 if self.mpath == 'true': 417 ↛ 418line 417 didn't jump to line 418, because the condition on line 417 was never true

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

419 dev_path = os.path.join("/dev/disk/by-scsid", self.dconf['SCSIid']) 

420 if not os.path.exists(dev_path): 

421 raise xs_errors.XenError('ConfigSCSIid') 

422 

423 devs = os.listdir(dev_path) 

424 for dev in devs: 

425 realdev = os.path.realpath(os.path.join(dev_path, dev)) 

426 util.set_scheduler(os.path.basename(realdev)) 

427 

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

429 keys = [] 

430 pbdref = None 

431 try: 

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

433 except: 

434 pass 

435 if 'SCSIid' in self.dconf: 

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

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

438 

439 # Remove iscsi_sessions and multipathed keys 

440 if pbdref is not None: 

441 if self.cmd == 'sr_detach': 

442 keys += ["multipathed", "iscsi_sessions"] 

443 for key in keys: 

444 try: 

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

446 except: 

447 pass 

448 

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

450 return 

451 

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

453 return 

454 

455 if iscsilib._checkTGT(self.targetIQN): 

456 try: 

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

458 if delete: 

459 iscsilib.delete(self.targetIQN) 

460 except util.CommandException as inst: 

461 raise xs_errors.XenError('ISCSIQueryDaemon', \ 

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

463 if iscsilib._checkTGT(self.targetIQN): 

464 raise xs_errors.XenError('ISCSIQueryDaemon', \ 

465 opterr='Failed to logout from target') 

466 

467 self.attached = False 

468 

469 def create(self, sr_uuid, size): 

470 # Check whether an SR already exists 

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

472 for sr in SRs: 

473 record = SRs[sr] 

474 sm_config = record["sm_config"] 

475 if 'targetIQN' in sm_config and \ 

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

477 raise xs_errors.XenError('SRInUse') 

478 self.attach(sr_uuid) 

479 # Wait up to MAX_TIMEOUT for devices to appear 

480 util.wait_for_path(self.path, MAX_TIMEOUT) 

481 

482 if self._loadvdis() > 0: 

483 scanrecord = SR.ScanRecord(self) 

484 scanrecord.synchronise() 

485 try: 

486 self.detach(sr_uuid) 

487 except: 

488 pass 

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

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

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

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

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

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

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

496 return 

497 

498 def delete(self, sr_uuid): 

499 self.detach(sr_uuid) 

500 return 

501 

502 def probe(self): 

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

504 Recs = {} 

505 for sr in SRs: 

506 record = SRs[sr] 

507 sm_config = record["sm_config"] 

508 if 'targetIQN' in sm_config and \ 

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

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

511 return self.srlist_toxml(Recs) 

512 

513 def scan(self, sr_uuid): 

514 if not self.passthrough: 

515 if not self.attached: 

516 raise xs_errors.XenError('SRUnavailable') 

517 self.refresh() 

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

519 self._loadvdis() 

520 self.physical_utilisation = self.physical_size 

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

522 if vdi.managed: 

523 self.physical_utilisation += vdi.size 

524 self.virtual_allocation = self.physical_utilisation 

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

526 

527 def vdi(self, uuid): 

528 return LUNperVDI.RAWVDI(self, uuid) 

529 

530 def _scan_IQNs(self): 

531 # Verify iSCSI target and port 

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

533 

534 # Test and set the initiatorname file 

535 iscsilib.ensure_daemon_running_ok(self.localIQN) 

536 

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

538 self.chappassword, 

539 interfaceArray=iscsilib.get_iscsi_interfaces()) 

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

541 self.print_entries(map) 

542 

543 def _attach_LUN_bylunid(self, lunid): 

544 if not self.attached: 

545 raise xs_errors.XenError('SRUnavailable') 

546 connected = [] 

547 for val in self.adapter: 

548 if val not in self.pathdict: 

549 continue 

550 rec = self.pathdict[val] 

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

552 realpath = os.path.realpath(path) 

553 host = self.adapter[val] 

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

555 

556 addDevice = True 

557 if realpath in self.devs: 

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

559 real_SCSIid = None 

560 try: 

561 real_SCSIid = scsiutil.getSCSIid(realpath) 

562 except: 

563 pass 

564 

565 if real_SCSIid is not None: 

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

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

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

569 if cur_SCSIid != real_SCSIid: 

570 # looks stale, remove it 

571 scsiutil.scsi_dev_ctrl(l, "remove") 

572 else: 

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

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

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

576 addDevice = False 

577 else: 

578 # looks stale, remove it 

579 scsiutil.scsi_dev_ctrl(l, "remove") 

580 

581 if addDevice: 

582 # add the device 

583 scsiutil.scsi_dev_ctrl(l, "add") 

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

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

586 continue 

587 connected.append(path) 

588 return connected 

589 

590 def _attach_LUN_byserialid(self, serialid): 

591 if not self.attached: 

592 raise xs_errors.XenError('SRUnavailable') 

593 connected = [] 

594 for val in self.adapter: 

595 if val not in self.pathdict: 

596 continue 

597 rec = self.pathdict[val] 

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

599 realpath = os.path.realpath(path) 

600 if realpath not in self.devs: 

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

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

603 continue 

604 connected.append(path) 

605 return connected 

606 

607 def _detach_LUN_bylunid(self, lunid, SCSIid): 

608 if not self.attached: 

609 raise xs_errors.XenError('SRUnavailable') 

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

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

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

613 for val in self.adapter: 

614 if val not in self.pathdict: 

615 continue 

616 rec = self.pathdict[val] 

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

618 realpath = os.path.realpath(path) 

619 if realpath in self.devs: 

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

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

622 # Wait for device to disappear 

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

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

625 MAX_LUNID_TIMEOUT) 

626 else: 

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

628 

629 def _attach_LUN_bySCSIid(self, SCSIid): 

630 if not self.attached: 

631 raise xs_errors.XenError('SRUnavailable') 

632 

633 path = self.mpathmodule.path(SCSIid) 

634 if not util.pathexists(path): 

635 self.refresh() 

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

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

638 % path) 

639 raise xs_errors.XenError('ISCSIDevice') 

640 

641 # This function queries the session for the attached LUNs 

642 def _loadvdis(self): 

643 count = 0 

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

645 return 0 

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

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

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

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

650 obj = self.vdi(uuid) 

651 obj._query(vdi_path, LUNid) 

652 self.vdis[uuid] = obj 

653 self.physical_size += obj.size 

654 count += 1 

655 return count 

656 

657 def refresh(self): 

658 for val in self.adapter: 

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

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

661 

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

663 def _getLUNbySMconfig(self, sm_config): 

664 if 'LUNid' not in sm_config: 

665 raise xs_errors.XenError('VDIUnavailable') 

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

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

668 raise xs_errors.XenError('VDIUnavailable') 

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

670 

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

672 # a dictionary of available LUNs on that target. 

673 def print_LUNs(self): 

674 self.LUNs = {} 

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

676 dom0_disks = util.dom0_disks() 

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

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

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

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

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

682 else: 

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

684 obj = self.vdi(self.uuid) 

685 obj._query(vdi_path, LUNid) 

686 self.LUNs[obj.uuid] = obj 

687 

688 def print_entries(self, map): 

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

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

691 dom.appendChild(element) 

692 count = 0 

693 for address, tpgt, iqn in map: 

694 entry = dom.createElement('TGT') 

695 element.appendChild(entry) 

696 subentry = dom.createElement('Index') 

697 entry.appendChild(subentry) 

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

699 subentry.appendChild(textnode) 

700 

701 try: 

702 # We always expect a port so this holds 

703 # regardless of IP version 

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

705 except: 

706 addr = address 

707 port = DEFAULT_PORT 

708 subentry = dom.createElement('IPAddress') 

709 entry.appendChild(subentry) 

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

711 subentry.appendChild(textnode) 

712 

713 if int(port) != DEFAULT_PORT: 

714 subentry = dom.createElement('Port') 

715 entry.appendChild(subentry) 

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

717 subentry.appendChild(textnode) 

718 

719 subentry = dom.createElement('TargetIQN') 

720 entry.appendChild(subentry) 

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

722 subentry.appendChild(textnode) 

723 count += 1 

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

725 

726 def srlist_toxml(self, SRs): 

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

728 element = dom.createElement("SRlist") 

729 dom.appendChild(element) 

730 

731 for val in SRs: 

732 record = SRs[val] 

733 entry = dom.createElement('SR') 

734 element.appendChild(entry) 

735 

736 subentry = dom.createElement("UUID") 

737 entry.appendChild(subentry) 

738 textnode = dom.createTextNode(val) 

739 subentry.appendChild(textnode) 

740 

741 subentry = dom.createElement("Target") 

742 entry.appendChild(subentry) 

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

744 subentry.appendChild(textnode) 

745 

746 subentry = dom.createElement("TargetIQN") 

747 entry.appendChild(subentry) 

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

749 subentry.appendChild(textnode) 

750 return dom.toprettyxml() 

751 

752 def match_lun(self, s): 

753 regex = re.compile("_") 

754 if regex.search(s, 0): 

755 return False 

756 regex = re.compile("LUN") 

757 return regex.search(s, 0)