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 

21from sm_typing import override 

22 

23import SR 

24import VDI 

25import util 

26import time 

27import LUNperVDI 

28import os 

29import sys 

30import re 

31import glob 

32import xml.dom.minidom 

33import scsiutil 

34import iscsilib 

35import xs_errors 

36 

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

38SECTOR_SHIFT = 9 

39DEFAULT_PORT = 3260 

40# 2^16 Max port number value 

41MAXPORT = 65535 

42MAX_TIMEOUT = 15 

43MAX_LUNID_TIMEOUT = 60 

44ISCSI_PROCNAME = "iscsi_tcp" 

45 

46 

47class BaseISCSISR(SR.SR): 

48 """ISCSI storage repository""" 

49 

50 @property 

51 def force_tapdisk(self): 

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

53 

54 @property 

55 def attached(self): 

56 if not self._attached: 

57 self._attached = False 

58 self._attached = iscsilib._checkTGT(self.targetIQN, self.target) 

59 return self._attached 

60 

61 @attached.setter 

62 def attached(self, value): 

63 self._attached = value 

64 

65 @property 

66 def pathdict(self): 

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

68 self._initPaths() 

69 return self._pathdict 

70 

71 @property 

72 def adapter(self): 

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

74 self._initPaths() 

75 return self._adapter 

76 

77 @adapter.setter 

78 def adapter(self, value): 

79 self._adapter = value 

80 

81 @property 

82 def devs(self): 

83 if not self._devs: 

84 self._initPaths() 

85 return self._devs 

86 

87 @property 

88 def tgtidx(self): 

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

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

91 self._initPaths() 

92 return self._tgtidx 

93 

94 @property 

95 def path(self): 

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

97 self._initPaths() 

98 return self._path 

99 

100 @property 

101 def address(self): 

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

103 self._initPaths() 

104 return self._address 

105 

106 @override 

107 @staticmethod 

108 def handles(type) -> bool: 

109 return False 

110 

111 def _synchroniseAddrList(self, addrlist) -> None: 

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

113 return 

114 change = False 

115 if 'multihomelist' not in self.dconf: 

116 change = True 

117 self.mlist = [] 

118 mstr = "" 

119 else: 

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

121 mstr = self.dconf['multihomelist'] 

122 for val in addrlist: 

123 if not val in self.mlist: 

124 self.mlist.append(val) 

125 if len(mstr): 

126 mstr += "," 

127 mstr += val 

128 change = True 

129 if change: 

130 pbd = None 

131 try: 

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

133 if pbd is not None: 

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

135 device_config['multihomelist'] = mstr 

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

137 except: 

138 pass 

139 

140 @override 

141 def load(self, sr_uuid) -> None: 

142 if self.force_tapdisk: 

143 self.sr_vditype = 'aio' 

144 else: 

145 self.sr_vditype = 'phy' 

146 self.discoverentry = 0 

147 self.default_vdi_visibility = False 

148 

149 # Required parameters 

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

151 raise xs_errors.XenError('ConfigTargetMissing') 

152 

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

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

155 try: 

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

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

158 else: 

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

160 except: 

161 raise xs_errors.XenError('ConfigISCSIIQNMissing') 

162 

163 # Check for empty string 

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

165 raise xs_errors.XenError('ConfigISCSIIQNMissing') 

166 

167 try: 

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

169 except: 

170 raise xs_errors.XenError('DNSError') 

171 

172 self.targetlist = self.target 

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

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

175 

176 # Optional parameters 

177 self.chapuser = "" 

178 self.chappassword = "" 

179 if 'chapuser' in self.dconf \ 

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

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

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

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

184 else: 

185 chappassword = self.dconf['chappassword'] 

186 

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

188 

189 self.incoming_chapuser = "" 

190 self.incoming_chappassword = "" 

191 if 'incoming_chapuser' in self.dconf \ 

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

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

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

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

196 else: 

197 incoming_chappassword = self.dconf['incoming_chappassword'] 

198 

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

200 

201 self.port = DEFAULT_PORT 

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

203 try: 

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

205 except: 

206 raise xs_errors.XenError('ISCSIPort') 

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

208 raise xs_errors.XenError('ISCSIPort') 

209 

210 # For backwards compatibility 

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

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

213 

214 self.multihomed = False 

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

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

217 self.multihomed = True 

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

219 self.multihomed = True 

220 

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

222 self._scan_IQNs() 

223 raise xs_errors.XenError('ConfigTargetIQNMissing') 

224 

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

226 

227 self._attached = None 

228 self._pathdict = None 

229 self._adapter = None 

230 self._devs = None 

231 self._tgtidx = None 

232 self._path = None 

233 self._address = None 

234 

235 def _initPaths(self): 

236 self._init_adapters() 

237 # Generate a list of all possible paths 

238 self._pathdict = {} 

239 addrlist = [] 

240 rec = {} 

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

242 rec['ipaddr'] = self.target 

243 rec['port'] = self.port 

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

245 key) 

246 self._pathdict[key] = rec 

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

248 self._tgtidx = key 

249 addrlist.append(key) 

250 

251 self._path = rec['path'] 

252 self._address = self.tgtidx 

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

254 return 

255 

256 if self.multihomed: 

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

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

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

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

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

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

263 rec = {} 

264 rec['ipaddr'] = ipaddr 

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

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

267 key) 

268 self._pathdict[key] = rec 

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

270 addrlist.append(key) 

271 

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

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

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

275 if key in self.adapter: 

276 self._tgtidx = key 

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

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

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

280 break 

281 self._address = self.tgtidx 

282 self._synchroniseAddrList(addrlist) 

283 

284 def _init_adapters(self) -> None: 

285 # Generate a list of active adapters 

286 ids = scsiutil._genHostList(ISCSI_PROCNAME) 

287 util.SMlog(ids) 

288 self._adapter = {} 

289 for host in ids: 

290 try: 

291 targetIQN = iscsilib.get_targetIQN(host) 

292 if targetIQN != self.targetIQN: 292 ↛ 293line 292 didn't jump to line 293, because the condition on line 292 was never true

293 continue 

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

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

296 self._adapter[entry] = host 

297 except: 

298 pass 

299 self._devs = scsiutil.cacheSCSIidentifiers() 

300 

301 @override 

302 def attach(self, sr_uuid) -> None: 

303 self._mpathHandle() 

304 

305 multiTargets = False 

306 

307 npaths = 0 

308 try: 

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

310 if pbdref: 310 ↛ 316line 310 didn't jump to line 316, because the condition on line 310 was never false

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

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

313 except: 

314 pass 

315 

316 if not self.attached or multiTargets: 316 ↛ 396line 316 didn't jump to line 396, because the condition on line 316 was never false

317 # Verify iSCSI target and port 

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

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

320 else: 

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

322 conn = False 

323 for val in targetlist: 323 ↛ 333line 323 didn't jump to line 333, because the loop on line 323 didn't complete

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

325 try: 

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

327 self.target = target 

328 self.port = int(port) 

329 conn = True 

330 break 

331 except: 

332 pass 

333 if not conn: 333 ↛ 334line 333 didn't jump to line 334, because the condition on line 333 was never true

334 raise xs_errors.XenError('ISCSITarget') 

335 

336 # Test and set the initiatorname file 

337 iscsilib.ensure_daemon_running_ok(self.localIQN) 

338 

339 # Check to see if auto attach was set 

340 if not iscsilib._checkTGT(self.targetIQN, tgt=self.target) or multiTargets: 340 ↛ 395line 340 didn't jump to line 395, because the condition on line 340 was never false

341 try: 

342 iqn_map = [] 

343 if 'any' != self.targetIQN: 343 ↛ 352line 343 didn't jump to line 352, because the condition on line 343 was never false

344 try: 

345 iqn_map = iscsilib.get_node_records(self.targetIQN) 

346 except: 

347 # Pass the exception that is thrown, when there 

348 # are no nodes 

349 pass 

350 

351 # Check our current target is in the map 

352 portal = '%s:%d' % (self.target, self.port) 

353 if len(iqn_map) == 0 or not any([x[0] for x in iqn_map if x[0] == portal]): 353 ↛ exit,   353 ↛ 3582 missed branches: 1) line 353 didn't run the list comprehension on line 353, 2) line 353 didn't jump to line 358, because the condition on line 353 was never false

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

355 self.chapuser, self.chappassword, 

356 self.targetIQN, 

357 iscsilib.get_iscsi_interfaces()) 

358 if len(iqn_map) == 0: 358 ↛ 359line 358 didn't jump to line 359, because the condition on line 358 was never true

359 self._scan_IQNs() 

360 raise xs_errors.XenError('ISCSIDiscovery', 

361 opterr='check target settings') 

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

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

364 try: 

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

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

367 continue 

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

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

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

371 self.chappassword, 

372 self.incoming_chapuser, 

373 self.incoming_chappassword, 

374 self.mpath == "true") 

375 npaths = npaths + 1 

376 except Exception as e: 

377 # Exceptions thrown in login are acknowledged, 

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

379 # paths in multipath may not be reachable 

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

381 raise 

382 else: 

383 pass 

384 

385 if not iscsilib._checkTGT(self.targetIQN, tgt=self.target): 385 ↛ 386line 385 didn't jump to line 386, because the condition on line 385 was never true

386 raise xs_errors.XenError('ISCSIDevice', \ 

387 opterr='during login') 

388 

389 # Allow the devices to settle 

390 time.sleep(5) 

391 

392 except util.CommandException as inst: 

393 raise xs_errors.XenError('ISCSILogin', \ 

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

395 self.attached = True 

396 self._initPaths() 

397 util._incr_iscsiSR_refcount(self.targetIQN, sr_uuid) 

398 IQNs = [] 

399 if "multiSession" in self.dconf: 

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

401 if len(iqn): 

402 IQNs.append(iqn.split(',')[2]) 

403 else: 

404 IQNs.append(self.targetIQN) 

405 

406 sessions = 0 

407 paths = iscsilib.get_IQN_paths() 

408 for path in paths: 408 ↛ 409line 408 didn't jump to line 409, because the loop on line 408 never started

409 try: 

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

411 sessions += 1 

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

413 except: 

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

415 + "iscsi_sessions value may be incorrect") 

416 

417 if pbdref: 417 ↛ 425line 417 didn't jump to line 425, because the condition on line 417 was never false

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

419 # we remove the key and add it 

420 self.session.xenapi.PBD.remove_from_other_config( 

421 pbdref, "iscsi_sessions") 

422 self.session.xenapi.PBD.add_to_other_config( 

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

424 

425 if 'SCSIid' in self.dconf: 425 ↛ exitline 425 didn't return from function 'attach', because the condition on line 425 was never false

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

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

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

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

430 # LUN may have been added to the SAN since the session was created 

431 iscsilib.refresh_luns(self.targetIQN, self.target) 

432 

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

434 raise xs_errors.XenError('ConfigSCSIid') 

435 

436 devs = os.listdir(dev_path) 

437 for dev in devs: 

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

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

440 

441 @override 

442 def detach(self, sr_uuid) -> None: 

443 self.detach_and_delete(sr_uuid, delete=False) 

444 

445 def detach_and_delete(self, sr_uuid, delete=True) -> None: 

446 keys = [] 

447 pbdref = None 

448 try: 

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

450 except: 

451 pass 

452 if 'SCSIid' in self.dconf: 

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

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

455 

456 # Remove iscsi_sessions and multipathed keys 

457 if pbdref is not None: 

458 if self.cmd == 'sr_detach': 

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

460 for key in keys: 

461 try: 

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

463 except: 

464 pass 

465 

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

467 return 

468 

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

470 return 

471 

472 if iscsilib._checkTGT(self.targetIQN): 

473 try: 

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

475 if delete: 

476 iscsilib.delete(self.targetIQN) 

477 except util.CommandException as inst: 

478 raise xs_errors.XenError('ISCSIQueryDaemon', \ 

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

480 if iscsilib._checkTGT(self.targetIQN): 

481 raise xs_errors.XenError('ISCSIQueryDaemon', \ 

482 opterr='Failed to logout from target') 

483 

484 self.attached = False 

485 

486 @override 

487 def create(self, sr_uuid, size) -> None: 

488 # Check whether an SR already exists 

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

490 for sr in SRs: 

491 record = SRs[sr] 

492 sm_config = record["sm_config"] 

493 if 'targetIQN' in sm_config and \ 

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

495 raise xs_errors.XenError('SRInUse') 

496 self.attach(sr_uuid) 

497 # Wait up to MAX_TIMEOUT for devices to appear 

498 util.wait_for_path(self.path, MAX_TIMEOUT) 

499 

500 if self._loadvdis() > 0: 

501 scanrecord = SR.ScanRecord(self) 

502 scanrecord.synchronise() 

503 try: 

504 self.detach(sr_uuid) 

505 except: 

506 pass 

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

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

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

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

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

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

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

514 return 

515 

516 @override 

517 def delete(self, sr_uuid) -> None: 

518 self.detach(sr_uuid) 

519 return 

520 

521 @override 

522 def probe(self) -> str: 

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

524 Recs = {} 

525 for sr in SRs: 

526 record = SRs[sr] 

527 sm_config = record["sm_config"] 

528 if 'targetIQN' in sm_config and \ 

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

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

531 return self.srlist_toxml(Recs) 

532 

533 @override 

534 def scan(self, sr_uuid) -> None: 

535 if not self.passthrough: 

536 if not self.attached: 

537 raise xs_errors.XenError('SRUnavailable') 

538 self.refresh() 

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

540 self._loadvdis() 

541 self.physical_utilisation = self.physical_size 

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

543 if vdi.managed: 

544 self.physical_utilisation += vdi.size 

545 self.virtual_allocation = self.physical_utilisation 

546 super(BaseISCSISR, self).scan(sr_uuid) 

547 

548 @override 

549 def vdi(self, uuid) -> VDI.VDI: 

550 return LUNperVDI.RAWVDI(self, uuid) 

551 

552 def _scan_IQNs(self): 

553 # Verify iSCSI target and port 

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

555 

556 # Test and set the initiatorname file 

557 iscsilib.ensure_daemon_running_ok(self.localIQN) 

558 

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

560 self.chappassword, 

561 interfaceArray=iscsilib.get_iscsi_interfaces()) 

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

563 self.print_entries(map) 

564 

565 def _attach_LUN_bylunid(self, lunid): 

566 if not self.attached: 

567 raise xs_errors.XenError('SRUnavailable') 

568 connected = [] 

569 for val in self.adapter: 

570 if val not in self.pathdict: 

571 continue 

572 rec = self.pathdict[val] 

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

574 realpath = os.path.realpath(path) 

575 host = self.adapter[val] 

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

577 

578 addDevice = True 

579 if realpath in self.devs: 

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

581 real_SCSIid = None 

582 try: 

583 real_SCSIid = scsiutil.getSCSIid(realpath) 

584 except: 

585 pass 

586 

587 if real_SCSIid is not None: 

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

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

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

591 if cur_SCSIid != real_SCSIid: 

592 # looks stale, remove it 

593 scsiutil.scsi_dev_ctrl(l, "remove") 

594 else: 

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

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

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

598 addDevice = False 

599 else: 

600 # looks stale, remove it 

601 scsiutil.scsi_dev_ctrl(l, "remove") 

602 

603 if addDevice: 

604 # add the device 

605 scsiutil.scsi_dev_ctrl(l, "add") 

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

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

608 continue 

609 connected.append(path) 

610 return connected 

611 

612 def _attach_LUN_byserialid(self, serialid): 

613 if not self.attached: 

614 raise xs_errors.XenError('SRUnavailable') 

615 connected = [] 

616 for val in self.adapter: 

617 if val not in self.pathdict: 

618 continue 

619 rec = self.pathdict[val] 

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

621 realpath = os.path.realpath(path) 

622 if realpath not in self.devs: 

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

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

625 continue 

626 connected.append(path) 

627 return connected 

628 

629 def _detach_LUN_bylunid(self, lunid, SCSIid): 

630 if not self.attached: 

631 raise xs_errors.XenError('SRUnavailable') 

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

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

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

635 for val in self.adapter: 

636 if val not in self.pathdict: 

637 continue 

638 rec = self.pathdict[val] 

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

640 realpath = os.path.realpath(path) 

641 if realpath in self.devs: 

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

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

644 # Wait for device to disappear 

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

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

647 MAX_LUNID_TIMEOUT) 

648 else: 

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

650 

651 def _attach_LUN_bySCSIid(self, SCSIid): 

652 if not self.attached: 

653 raise xs_errors.XenError('SRUnavailable') 

654 

655 path = self.mpathmodule.path(SCSIid) 

656 if not util.pathexists(path): 

657 self.refresh() 

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

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

660 % path) 

661 raise xs_errors.XenError('ISCSIDevice') 

662 

663 # This function queries the session for the attached LUNs 

664 def _loadvdis(self): 

665 count = 0 

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

667 return 0 

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

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

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

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

672 obj = self.vdi(uuid) 

673 obj._query(vdi_path, LUNid) 

674 self.vdis[uuid] = obj 

675 self.physical_size += obj.size 

676 count += 1 

677 return count 

678 

679 def refresh(self): 

680 for val in self.adapter: 

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

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

683 

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

685 def _getLUNbySMconfig(self, sm_config): 

686 if 'LUNid' not in sm_config: 

687 raise xs_errors.XenError('VDIUnavailable') 

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

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

690 raise xs_errors.XenError('VDIUnavailable') 

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

692 

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

694 # a dictionary of available LUNs on that target. 

695 def print_LUNs(self): 

696 self.LUNs = {} 

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

698 dom0_disks = util.dom0_disks() 

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

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

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

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

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

704 else: 

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

706 obj = self.vdi(self.uuid) 

707 obj._query(vdi_path, LUNid) 

708 self.LUNs[obj.uuid] = obj 

709 

710 def print_entries(self, map): 

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

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

713 dom.appendChild(element) 

714 count = 0 

715 for address, tpgt, iqn in map: 

716 entry = dom.createElement('TGT') 

717 element.appendChild(entry) 

718 subentry = dom.createElement('Index') 

719 entry.appendChild(subentry) 

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

721 subentry.appendChild(textnode) 

722 

723 try: 

724 # We always expect a port so this holds 

725 # regardless of IP version 

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

727 except: 

728 addr = address 

729 port = DEFAULT_PORT 

730 subentry = dom.createElement('IPAddress') 

731 entry.appendChild(subentry) 

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

733 subentry.appendChild(textnode) 

734 

735 if int(port) != DEFAULT_PORT: 

736 subentry = dom.createElement('Port') 

737 entry.appendChild(subentry) 

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

739 subentry.appendChild(textnode) 

740 

741 subentry = dom.createElement('TargetIQN') 

742 entry.appendChild(subentry) 

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

744 subentry.appendChild(textnode) 

745 count += 1 

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

747 

748 def srlist_toxml(self, SRs): 

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

750 element = dom.createElement("SRlist") 

751 dom.appendChild(element) 

752 

753 for val in SRs: 

754 record = SRs[val] 

755 entry = dom.createElement('SR') 

756 element.appendChild(entry) 

757 

758 subentry = dom.createElement("UUID") 

759 entry.appendChild(subentry) 

760 textnode = dom.createTextNode(val) 

761 subentry.appendChild(textnode) 

762 

763 subentry = dom.createElement("Target") 

764 entry.appendChild(subentry) 

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

766 subentry.appendChild(textnode) 

767 

768 subentry = dom.createElement("TargetIQN") 

769 entry.appendChild(subentry) 

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

771 subentry.appendChild(textnode) 

772 return dom.toprettyxml() 

773 

774 def match_lun(self, s): 

775 regex = re.compile("_") 

776 if regex.search(s, 0): 

777 return False 

778 regex = re.compile("LUN") 

779 return regex.search(s, 0)