Coverage for libs/sm/drivers/LVHDoISCSISR.py: 38%

404 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2026-07-10 07:59 +0000

1# Copyright (C) Citrix Systems Inc. 

2# 

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

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

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

6# 

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

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

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

10# GNU Lesser General Public License for more details. 

11# 

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

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

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

15# 

16# LVHDoISCSISR: LVHD over ISCSI software initiator SR driver 

17# matches with drivers/LVHDoISCSISR 

18# 

19 

20from sm import SR 

21from sm.drivers import LVHDSR 

22from sm import BaseISCSI 

23from sm.core import util 

24from sm.core import scsiutil 

25from sm import lvutil 

26import time 

27import os 

28import sys 

29from sm.core import xs_errors 

30import xmlrpc.client 

31from sm.core import mpath_cli 

32from sm.core import iscsi 

33import glob 

34import copy 

35import xml.dom.minidom 

36 

37CAPABILITIES = ["SR_PROBE", "SR_UPDATE", "SR_METADATA", "SR_TRIM", "SR_CACHING", 

38 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", 

39 "VDI_GENERATE_CONFIG", "VDI_CLONE", "VDI_SNAPSHOT", 

40 "VDI_RESIZE", "ATOMIC_PAUSE", "VDI_RESET_ON_BOOT/2", 

41 "VDI_UPDATE", "VDI_MIRROR", "VDI_CONFIG_CBT", 

42 "VDI_ACTIVATE", "VDI_DEACTIVATE"] 

43 

44CONFIGURATION = [['SCSIid', 'The scsi_id of the destination LUN'], \ 

45 ['target', 'IP address or hostname of the iSCSI target'], \ 

46 ['targetIQN', 'The IQN of the target LUN group to be attached'], \ 

47 ['chapuser', 'The username to be used during CHAP authentication'], \ 

48 ['chappassword', 'The password to be used during CHAP authentication'], \ 

49 ['incoming_chapuser', 'The incoming username to be used during bi-directional CHAP authentication (optional)'], \ 

50 ['incoming_chappassword', 'The incoming password to be used during bi-directional CHAP authentication (optional)'], \ 

51 ['port', 'The network port number on which to query the target'], \ 

52 ['multihomed', 'Enable multi-homing to this target, true or false (optional, defaults to same value as host.other_config:multipathing)'], \ 

53 ['usediscoverynumber', 'The specific iscsi record index to use. (optional)'], \ 

54 ['allocation', 'Valid values are thick or thin (optional, defaults to thick)']] 

55 

56DRIVER_INFO = { 

57 'name': 'LVHD over iSCSI', 

58 'description': 'SR plugin which represents disks as Logical Volumes within a Volume Group created on an iSCSI LUN', 

59 'vendor': 'Citrix Systems Inc', 

60 'copyright': '(C) 2008 Citrix Systems Inc', 

61 'driver_version': '1.0', 

62 'required_api_version': '1.0', 

63 'capabilities': CAPABILITIES, 

64 'configuration': CONFIGURATION 

65 } 

66 

67 

68class LVHDoISCSISR(LVHDSR.LVHDSR): 

69 """LVHD over ISCSI storage repository""" 

70 

71 @staticmethod 

72 def handles(type): 

73 if type == "lvmoiscsi": 

74 return True 

75 if type == "lvhdoiscsi": 

76 return True 

77 return False 

78 

79 def load(self, sr_uuid): 

80 if not sr_uuid: 80 ↛ 82line 80 didn't jump to line 82, because the condition on line 80 was never true

81 # This is a probe call, generate a temp sr_uuid 

82 sr_uuid = util.gen_uuid() 

83 

84 # If this is a vdi command, don't initialise SR 

85 if util.isVDICommand(self.original_srcmd.cmd): 

86 self.SCSIid = self.dconf['SCSIid'] 

87 else: 

88 self.create_iscsi_sessions(sr_uuid) 

89 

90 LVHDSR.LVHDSR.load(self, sr_uuid) 

91 

92 def create_iscsi_sessions(self, sr_uuid): 

93 if 'target' in self.original_srcmd.dconf: 93 ↛ 95line 93 didn't jump to line 95, because the condition on line 93 was never false

94 self.original_srcmd.dconf['targetlist'] = self.original_srcmd.dconf['target'] 

95 baseiscsi = BaseISCSI.BaseISCSISR(self.original_srcmd, sr_uuid) 

96 self.iscsiSRs = [] 

97 self.iscsiSRs.append(baseiscsi) 

98 saved_exc = None 

99 targets = self.dconf['target'].split(',') 

100 if len(targets) > 1 or self.dconf['targetIQN'] == "*": 

101 # Instantiate multiple sessions 

102 self.iscsiSRs = [] 

103 if self.dconf['targetIQN'] == "*": 103 ↛ 106line 103 didn't jump to line 106, because the condition on line 103 was never false

104 IQN = "any" 

105 else: 

106 IQN = self.dconf['targetIQN'] 

107 dict = {} 

108 IQNstring = "" 

109 IQNs = [] 

110 try: 

111 if 'multiSession' in self.dconf: 

112 IQNs = self.dconf['multiSession'].split("|") 

113 for IQN in IQNs: 

114 if IQN: 

115 dict[IQN] = "" 

116 else: 

117 try: 

118 IQNs.remove(IQN) 

119 except: 

120 # Exceptions are not expected but just in case 

121 pass 

122 # Order in multiSession must be preserved. It is important for dual-controllers. 

123 # IQNstring cannot be built with a dictionary iteration because of this 

124 IQNstring = self.dconf['multiSession'] 

125 else: 

126 for tgt in targets: 126 ↛ 145line 126 didn't jump to line 145, because the loop on line 126 didn't complete

127 try: 

128 tgt_ip = util._convertDNS(tgt) 

129 except: 

130 raise xs_errors.XenError('DNSError') 

131 iscsi.ensure_daemon_running_ok(baseiscsi.localIQN) 

132 map = iscsi.discovery(tgt_ip, baseiscsi.port, baseiscsi.chapuser, baseiscsi.chappassword, targetIQN=IQN) 

133 util.SMlog("Discovery for IP %s returned %s" % (tgt, map)) 

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

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

136 (ipaddr, port) = iscsi.parse_IP_port(portal) 

137 try: 

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

139 except: 

140 util.SMlog("Target Not reachable: (%s:%s)" % (ipaddr, port)) 

141 continue 

142 key = "%s,%s,%s" % (ipaddr, port, iqn) 

143 dict[key] = "" 

144 # Again, do not mess up with IQNs order. Dual controllers will benefit from that 

145 if IQNstring == "": 145 ↛ 147line 145 didn't jump to line 147, because the condition on line 145 was never true

146 # Compose the IQNstring first 

147 for key in dict.keys(): 

148 IQNstring += "%s|" % key 

149 # Reinitialize and store iterator 

150 key_iterator = iter(dict.keys()) 

151 else: 

152 key_iterator = IQNs 

153 

154 # Now load the individual iSCSI base classes 

155 for key in key_iterator: 

156 (ipaddr, port, iqn) = key.split(',') 

157 srcmd_copy = copy.deepcopy(self.original_srcmd) 

158 srcmd_copy.dconf['target'] = ipaddr 

159 srcmd_copy.dconf['targetIQN'] = iqn 

160 srcmd_copy.dconf['multiSession'] = IQNstring 

161 util.SMlog("Setting targetlist: %s" % srcmd_copy.dconf['targetlist']) 

162 self.iscsiSRs.append(BaseISCSI.BaseISCSISR(srcmd_copy, sr_uuid)) 

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

164 if pbd is not None and 'multiSession' not in self.dconf: 164 ↛ 165line 164 didn't jump to line 165, because the condition on line 164 was never true

165 dconf = self.session.xenapi.PBD.get_device_config(pbd) 

166 dconf['multiSession'] = IQNstring 

167 self.session.xenapi.PBD.set_device_config(pbd, dconf) 

168 except Exception as exc: 

169 util.logException("LVHDoISCSISR.load") 

170 saved_exc = exc 

171 try: 

172 self.iscsi = self.iscsiSRs[0] 

173 except IndexError as exc: 

174 if isinstance(saved_exc, xs_errors.SROSError): 

175 raise saved_exc # pylint: disable-msg=E0702 

176 elif isinstance(saved_exc, Exception): 176 ↛ 179line 176 didn't jump to line 179, because the condition on line 176 was never false

177 raise xs_errors.XenError('SMGeneral', str(saved_exc)) 

178 else: 

179 raise xs_errors.XenError('SMGeneral', str(exc)) 

180 # Be extremely careful not to throw exceptions here since this function 

181 # is the main one used by all operations including probing and creating 

182 pbd = None 

183 try: 

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

185 except: 

186 pass 

187 # Apart from the upgrade case, user must specify a SCSIid 

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

189 # Dual controller issue 

190 self.LUNs = {} # Dict for LUNs from all the iscsi objects 

191 for ii in range(0, len(self.iscsiSRs)): 

192 self.iscsi = self.iscsiSRs[ii] 

193 self._LUNprint(sr_uuid) 

194 for key in self.iscsi.LUNs: 

195 self.LUNs[key] = self.iscsi.LUNs[key] 

196 self.print_LUNs_XML() 

197 self.iscsi = self.iscsiSRs[0] # back to original value 

198 raise xs_errors.XenError('ConfigSCSIid') 

199 self.SCSIid = self.dconf['SCSIid'] 

200 # This block checks if the first iscsi target contains the right SCSIid. 

201 # If not it scans the other iscsi targets because chances are that more 

202 # than one controller is present 

203 dev_match = False 

204 forced_login = False 

205 # No need to check if only one iscsi target is present 

206 if len(self.iscsiSRs) == 1: 

207 pass 

208 else: 

209 target_success = False 

210 attempt_discovery = False 

211 for iii in range(0, len(self.iscsiSRs)): 

212 # Check we didn't leave any iscsi session open 

213 # If exceptions happened before, the cleanup function has worked on the right target. 

214 if forced_login == True: 

215 try: 

216 iscsi.ensure_daemon_running_ok(self.iscsi.localIQN) 

217 iscsi.logout(self.iscsi.target, self.iscsi.targetIQN) 

218 forced_login = False 

219 except: 

220 raise xs_errors.XenError('ISCSILogout') 

221 self.iscsi = self.iscsiSRs[iii] 

222 util.SMlog("path %s" % self.iscsi.path) 

223 util.SMlog("iscsci data: targetIQN %s, portal %s" % (self.iscsi.targetIQN, self.iscsi.target)) 

224 iscsi.ensure_daemon_running_ok(self.iscsi.localIQN) 

225 if not iscsi._checkTGT(self.iscsi.targetIQN, self.iscsi.target): 225 ↛ 265line 225 didn't jump to line 265, because the condition on line 225 was never false

226 attempt_discovery = True 

227 try: 

228 # Ensure iscsi db has been populated 

229 map = iscsi.discovery( 

230 self.iscsi.target, 

231 self.iscsi.port, 

232 self.iscsi.chapuser, 

233 self.iscsi.chappassword, 

234 targetIQN=self.iscsi.targetIQN) 

235 if len(map) == 0: 235 ↛ 236line 235 didn't jump to line 236, because the condition on line 235 was never true

236 util.SMlog("Discovery for iscsi data targetIQN %s," 

237 " portal %s returned empty list" 

238 " Trying another path if available" % 

239 (self.iscsi.targetIQN, 

240 self.iscsi.target)) 

241 continue 

242 except: 

243 util.SMlog("Discovery failed for iscsi data targetIQN" 

244 " %s, portal %s. Trying another path if" 

245 " available" % 

246 (self.iscsi.targetIQN, self.iscsi.target)) 

247 continue 

248 try: 

249 iscsi.login(self.iscsi.target, 

250 self.iscsi.targetIQN, 

251 self.iscsi.chapuser, 

252 self.iscsi.chappassword, 

253 self.iscsi.incoming_chapuser, 

254 self.iscsi.incoming_chappassword, 

255 self.mpath == "true") 

256 except: 

257 util.SMlog("Login failed for iscsi data targetIQN %s," 

258 " portal %s. Trying another path" 

259 " if available" % 

260 (self.iscsi.targetIQN, self.iscsi.target)) 

261 continue 

262 target_success = True 

263 forced_login = True 

264 # A session should be active. 

265 if not util.wait_for_path(self.iscsi.path, BaseISCSI.MAX_TIMEOUT): 265 ↛ 266line 265 didn't jump to line 266, because the condition on line 265 was never true

266 util.SMlog("%s has no associated LUNs" % self.iscsi.targetIQN) 

267 continue 

268 scsiid_path = "/dev/disk/by-id/scsi-" + self.SCSIid 

269 if not util.wait_for_path(scsiid_path, BaseISCSI.MAX_TIMEOUT): 269 ↛ 270line 269 didn't jump to line 270, because the condition on line 269 was never true

270 util.SMlog("%s not found" % scsiid_path) 

271 continue 

272 for file in filter(self.iscsi.match_lun, util.listdir(self.iscsi.path)): 272 ↛ 273line 272 didn't jump to line 273, because the loop on line 272 never started

273 lun_path = os.path.join(self.iscsi.path, file) 

274 lun_dev = scsiutil.getdev(lun_path) 

275 try: 

276 lun_scsiid = scsiutil.getSCSIid(lun_dev) 

277 except: 

278 util.SMlog("getSCSIid failed on %s in iscsi %s: LUN" 

279 " offline or iscsi path down" % 

280 (lun_dev, self.iscsi.path)) 

281 continue 

282 util.SMlog("dev from lun %s %s" % (lun_dev, lun_scsiid)) 

283 if lun_scsiid == self.SCSIid: 

284 util.SMlog("lun match in %s" % self.iscsi.path) 

285 dev_match = True 

286 # No more need to raise ISCSITarget exception. 

287 # Resetting attempt_discovery 

288 attempt_discovery = False 

289 break 

290 if dev_match: 290 ↛ 291line 290 didn't jump to line 291, because the condition on line 290 was never true

291 if iii == 0: 

292 break 

293 util.SMlog("IQN reordering needed") 

294 new_iscsiSRs = [] 

295 IQNs = {} 

296 IQNstring = "" 

297 # iscsiSRs can be seen as a circular buffer: the head now is the matching one 

298 for kkk in list(range(iii, len(self.iscsiSRs))) + list(range(0, iii)): 

299 new_iscsiSRs.append(self.iscsiSRs[kkk]) 

300 ipaddr = self.iscsiSRs[kkk].target 

301 port = self.iscsiSRs[kkk].port 

302 iqn = self.iscsiSRs[kkk].targetIQN 

303 key = "%s,%s,%s" % (ipaddr, port, iqn) 

304 # The final string must preserve the order without repetition 

305 if key not in IQNs: 

306 IQNs[key] = "" 

307 IQNstring += "%s|" % key 

308 util.SMlog("IQNstring is now %s" % IQNstring) 

309 self.iscsiSRs = new_iscsiSRs 

310 util.SMlog("iqn %s is leading now" % self.iscsiSRs[0].targetIQN) 

311 # Updating pbd entry, if any 

312 try: 

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

314 if pbd is not None and 'multiSession' in self.dconf: 

315 util.SMlog("Updating multiSession in PBD") 

316 dconf = self.session.xenapi.PBD.get_device_config(pbd) 

317 dconf['multiSession'] = IQNstring 

318 self.session.xenapi.PBD.set_device_config(pbd, dconf) 

319 except: 

320 pass 

321 break 

322 if not target_success and attempt_discovery: 322 ↛ 323line 322 didn't jump to line 323, because the condition on line 322 was never true

323 raise xs_errors.XenError('ISCSITarget') 

324 

325 # Check for any unneeded open iscsi sessions 

326 if forced_login == True: 326 ↛ exitline 326 didn't return from function 'create_iscsi_sessions', because the condition on line 326 was never false

327 try: 

328 iscsi.ensure_daemon_running_ok(self.iscsi.localIQN) 

329 iscsi.logout(self.iscsi.target, self.iscsi.targetIQN) 

330 forced_login = False 

331 except: 

332 raise xs_errors.XenError('ISCSILogout') 

333 

334 def print_LUNs_XML(self): 

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

336 element = dom.createElement("iscsi-target") 

337 dom.appendChild(element) 

338 for uuid in self.LUNs: 

339 val = self.LUNs[uuid] 

340 entry = dom.createElement('LUN') 

341 element.appendChild(entry) 

342 

343 for attr in ('vendor', 'serial', 'LUNid', \ 

344 'size', 'SCSIid'): 

345 try: 

346 aval = getattr(val, attr) 

347 except AttributeError: 

348 continue 

349 

350 if aval: 

351 subentry = dom.createElement(attr) 

352 entry.appendChild(subentry) 

353 textnode = dom.createTextNode(str(aval)) 

354 subentry.appendChild(textnode) 

355 

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

357 

358 def _getSCSIid_from_LUN(self, sr_uuid): 

359 was_attached = True 

360 self.iscsi.attach(sr_uuid) 

361 dev = self.dconf['LUNid'].split(',') 

362 if len(dev) > 1: 

363 raise xs_errors.XenError('LVMOneLUN') 

364 path = os.path.join(self.iscsi.path, "LUN%s" % dev[0]) 

365 if not util.wait_for_path(path, BaseISCSI.MAX_TIMEOUT): 

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

367 try: 

368 SCSIid = scsiutil.getSCSIid(path) 

369 except: 

370 raise xs_errors.XenError('InvalidDev') 

371 self.iscsi.detach(sr_uuid) 

372 return SCSIid 

373 

374 def _LUNprint(self, sr_uuid): 

375 if self.iscsi.attached: 

376 # Force a rescan on the bus. 

377 self.iscsi.refresh() 

378# time.sleep(5) 

379# Now call attach (handles the refcounting + session activa) 

380 self.iscsi.attach(sr_uuid) 

381 

382 util.SMlog("LUNprint: waiting for path: %s" % self.iscsi.path) 

383 if util.wait_for_path("%s/LUN*" % self.iscsi.path, BaseISCSI.MAX_TIMEOUT): 

384 try: 

385 adapter = self.iscsi.adapter[self.iscsi.address] 

386 util.SMlog("adapter=%s" % adapter) 

387 

388 # find a scsi device on which to issue a report luns command: 

389 devs = glob.glob("%s/LUN*" % self.iscsi.path) 

390 sgdevs = [] 

391 for i in devs: 

392 sgdevs.append(int(i.split("LUN")[1])) 

393 sgdevs.sort() 

394 sgdev = "%s/LUN%d" % (self.iscsi.path, sgdevs[0]) 

395 

396 # issue a report luns: 

397 luns = util.pread2(["/usr/bin/sg_luns", "-q", sgdev]).split('\n') 

398 nluns = len(luns) - 1 # remove the line relating to the final \n 

399 

400 # make sure we've got that many sg devices present 

401 for i in range(0, 30): 

402 luns = scsiutil._dosgscan() 

403 sgdevs = [r for r in luns if r[1] == adapter] 

404 if len(sgdevs) >= nluns: 

405 util.SMlog("Got all %d sg devices" % nluns) 

406 break 

407 else: 

408 util.SMlog("Got %d sg devices - expecting %d" % (len(sgdevs), nluns)) 

409 time.sleep(1) 

410 

411 if os.path.exists("/sbin/udevsettle"): 

412 util.pread2(["/sbin/udevsettle"]) 

413 else: 

414 util.pread2(["/sbin/udevadm", "settle"]) 

415 except: 

416 util.SMlog("Generic exception caught. Pass") 

417 pass # Make sure we don't break the probe... 

418 

419 self.iscsi.print_LUNs() 

420 self.iscsi.detach(sr_uuid) 

421 

422 def create(self, sr_uuid, size): 

423 # Check SCSIid not already in use by other PBDs 

424 if util.test_SCSIid(self.session, sr_uuid, self.SCSIid): 

425 raise xs_errors.XenError('SRInUse') 

426 

427 self.iscsi.attach(sr_uuid) 

428 try: 

429 self.iscsi._attach_LUN_bySCSIid(self.SCSIid) 

430 self._pathrefresh(LVHDoISCSISR) 

431 LVHDSR.LVHDSR.create(self, sr_uuid, size) 

432 except Exception as inst: 

433 self.iscsi.detach(sr_uuid) 

434 raise xs_errors.XenError("SRUnavailable", opterr=inst) 

435 self.iscsi.detach(sr_uuid) 

436 

437 def delete(self, sr_uuid): 

438 self._pathrefresh(LVHDoISCSISR) 

439 LVHDSR.LVHDSR.delete(self, sr_uuid) 

440 for i in self.iscsiSRs: 

441 i.detach(sr_uuid) 

442 

443 def attach(self, sr_uuid): 

444 try: 

445 connected = False 

446 stored_exception = None 

447 for i in self.iscsiSRs: 

448 try: 

449 i.attach(sr_uuid) 

450 except xs_errors.SROSError as inst: 

451 # Some iscsi objects can fail login/discovery but not all. Storing exception 

452 if inst.errno in [141, 83]: 

453 util.SMlog("Connection failed for target %s, continuing.." % i.target) 

454 stored_exception = inst 

455 continue 

456 else: 

457 raise 

458 else: 

459 connected = True 

460 

461 i._attach_LUN_bySCSIid(self.SCSIid) 

462 

463 # Check if at least one iscsi succeeded 

464 if not connected and stored_exception: 464 ↛ 466line 464 didn't jump to line 466, because the condition on line 464 was never true

465 # pylint: disable=raising-bad-type 

466 raise stored_exception 

467 

468 if 'multiSession' in self.dconf: 468 ↛ 473line 468 didn't jump to line 473, because the condition on line 468 was never false

469 # Force a manual bus refresh 

470 for a in self.iscsi.adapter: 470 ↛ 471line 470 didn't jump to line 471, because the loop on line 470 never started

471 scsiutil.rescan([self.iscsi.adapter[a]]) 

472 

473 self._pathrefresh(LVHDoISCSISR) 

474 

475 # Check that we only have PVs for the volume group with the expected SCSI ID 

476 lvutil.checkPVScsiIds(self.vgname, self.SCSIid) 

477 

478 LVHDSR.LVHDSR.attach(self, sr_uuid) 

479 except Exception as inst: 

480 for i in self.iscsiSRs: 

481 i.detach(sr_uuid) 

482 

483 # If we already have a proper error just raise it 

484 if isinstance(inst, xs_errors.SROSError): 

485 raise 

486 

487 raise xs_errors.XenError("SRUnavailable", opterr=inst) 

488 

489 self._setMultipathableFlag(SCSIid=self.SCSIid) 

490 

491 def detach(self, sr_uuid): 

492 LVHDSR.LVHDSR.detach(self, sr_uuid) 

493 for i in self.iscsiSRs: 

494 util.SMlog(f'Detaching {i}') 

495 i.detach(sr_uuid) 

496 

497 def scan(self, sr_uuid): 

498 self._pathrefresh(LVHDoISCSISR) 

499 if self.mpath == "true": 

500 for i in self.iscsiSRs: 

501 try: 

502 i.attach(sr_uuid) 

503 except xs_errors.SROSError: 

504 util.SMlog("Connection failed for target %s, continuing.." % i.target) 

505 LVHDSR.LVHDSR.scan(self, sr_uuid) 

506 

507 def probe(self): 

508 self.uuid = util.gen_uuid() 

509 

510 # When multipathing is enabled, since we don't refcount the multipath maps, 

511 # we should not attempt to do the iscsi.attach/detach when the map is already present, 

512 # as this will remove it (which may well be in use). 

513 if self.mpath == 'true' and 'SCSIid' in self.dconf: 

514 maps = [] 

515 try: 

516 maps = mpath_cli.list_maps() 

517 except: 

518 pass 

519 

520 if self.dconf['SCSIid'] in maps: 

521 raise xs_errors.XenError('SRInUse') 

522 

523 self.iscsi.attach(self.uuid) 

524 self.iscsi._attach_LUN_bySCSIid(self.SCSIid) 

525 self._pathrefresh(LVHDoISCSISR) 

526 out = LVHDSR.LVHDSR.probe(self) 

527 self.iscsi.detach(self.uuid) 

528 return out 

529 

530 def check_sr(self, sr_uuid): 

531 """Hook to check SR health""" 

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

533 if pbdref: 

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

535 if util.sessions_less_than_targets(other_config, self.dconf): 

536 self.create_iscsi_sessions(sr_uuid) 

537 for iscsi in self.iscsiSRs: 

538 try: 

539 iscsi.attach(sr_uuid) 

540 except xs_errors.SROSError: 

541 util.SMlog("Failed to attach iSCSI target") 

542 

543 def vdi(self, uuid): 

544 return LVHDoISCSIVDI(self, uuid) 

545 

546 

547class LVHDoISCSIVDI(LVHDSR.LVHDVDI): 

548 def generate_config(self, sr_uuid, vdi_uuid): 

549 util.SMlog("LVHDoISCSIVDI.generate_config") 

550 if not lvutil._checkLV(self.path): 

551 raise xs_errors.XenError('VDIUnavailable') 

552 dict = {} 

553 self.sr.dconf['localIQN'] = self.sr.iscsi.localIQN 

554 self.sr.dconf['multipathing'] = self.sr.mpath 

555 self.sr.dconf['multipathhandle'] = self.sr.mpathhandle 

556 dict['device_config'] = self.sr.dconf 

557 if 'chappassword_secret' in dict['device_config']: 

558 s = util.get_secret(self.session, dict['device_config']['chappassword_secret']) 

559 del dict['device_config']['chappassword_secret'] 

560 dict['device_config']['chappassword'] = s 

561 dict['sr_uuid'] = sr_uuid 

562 dict['vdi_uuid'] = vdi_uuid 

563 dict['command'] = 'vdi_attach_from_config' 

564 # Return the 'config' encoded within a normal XMLRPC response so that 

565 # we can use the regular response/error parsing code. 

566 config = xmlrpc.client.dumps(tuple([dict]), "vdi_attach_from_config") 

567 return xmlrpc.client.dumps((config, ), "", True) 

568 

569 def attach_from_config(self, sr_uuid, vdi_uuid): 

570 util.SMlog("LVHDoISCSIVDI.attach_from_config") 

571 try: 

572 self.sr.iscsi.attach(sr_uuid) 

573 self.sr.iscsi._attach_LUN_bySCSIid(self.sr.SCSIid) 

574 return LVHDSR.LVHDVDI.attach(self, sr_uuid, vdi_uuid) 

575 except: 

576 util.logException("LVHDoISCSIVDI.attach_from_config") 

577 raise xs_errors.XenError('SRUnavailable', \ 

578 opterr='Unable to attach the heartbeat disk') 

579 

580# SR registration at import 

581SR.registerSR(LVHDoISCSISR)