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# LVMoISCSISR: LVM over ISCSI software initiator SR driver 

19# 

20 

21from sm_typing import override 

22 

23import SR 

24import VDI 

25import LVMSR 

26import BaseISCSI 

27import SRCommand 

28import util 

29import scsiutil 

30import lvutil 

31import lock 

32import time 

33import os 

34import sys 

35import xs_errors 

36import xmlrpc.client 

37import mpath_cli 

38import iscsilib 

39import glob 

40import copy 

41import xml.dom.minidom 

42from iscsilib import iscsi_lock, iscsi_try_lock 

43 

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

45 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", 

46 "VDI_GENERATE_CONFIG", "VDI_CLONE", "VDI_SNAPSHOT", 

47 "VDI_RESIZE", "ATOMIC_PAUSE", "VDI_RESET_ON_BOOT/2", 

48 "VDI_UPDATE", "VDI_MIRROR", "VDI_CONFIG_CBT", 

49 "VDI_ACTIVATE", "VDI_DEACTIVATE"] 

50 

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

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

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

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

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

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

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

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

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

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

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

62 

63DRIVER_INFO = { 

64 'name': 'LVM over iSCSI', 

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

66 'vendor': 'Citrix Systems Inc', 

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

68 'driver_version': '1.0', 

69 'required_api_version': '1.0', 

70 'capabilities': CAPABILITIES, 

71 'configuration': CONFIGURATION 

72 } 

73 

74 

75class LVMoISCSISR(LVMSR.LVMSR): 

76 """LVM over ISCSI storage repository""" 

77 

78 @override 

79 @staticmethod 

80 def handles(type) -> bool: 

81 if __name__ == '__main__': 

82 name = sys.argv[0] 

83 else: 

84 name = __name__ 

85 if name.endswith("LVMoISCSISR"): 

86 return type == "lvmoiscsi" 

87 if type == "lvhdoiscsi": 

88 return True 

89 return False 

90 

91 @override 

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

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

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

95 sr_uuid = util.gen_uuid() 

96 

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

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

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

100 else: 

101 self.create_iscsi_sessions(sr_uuid) 

102 

103 LVMSR.LVMSR.load(self, sr_uuid) 

104 

105 def create_iscsi_sessions(self, sr_uuid): 

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

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

108 iscsi = BaseISCSI.BaseISCSISR(self.original_srcmd, sr_uuid) 

109 self.iscsiSRs = [] 

110 self.iscsiSRs.append(iscsi) 

111 saved_exc = None 

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

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

114 # Instantiate multiple sessions 

115 self.iscsiSRs = [] 

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

117 IQN = "any" 

118 else: 

119 IQN = self.dconf['targetIQN'] 

120 dict = {} 

121 IQNstring = "" 

122 IQNs = [] 

123 try: 

124 if 'multiSession' in self.dconf: 

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

126 for IQN in IQNs: 

127 if IQN: 

128 dict[IQN] = "" 

129 else: 

130 try: 

131 IQNs.remove(IQN) 

132 except: 

133 # Exceptions are not expected but just in case 

134 pass 

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

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

137 IQNstring = self.dconf['multiSession'] 

138 else: 

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

140 try: 

141 tgt_ip = util._convertDNS(tgt) 

142 except: 

143 raise xs_errors.XenError('DNSError') 

144 iscsilib.ensure_daemon_running_ok(iscsi.localIQN) 

145 map = iscsilib.discovery(tgt_ip, iscsi.port, iscsi.chapuser, iscsi.chappassword, targetIQN=IQN) 

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

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

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

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

150 try: 

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

152 except: 

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

154 continue 

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

156 dict[key] = "" 

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

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

159 # Compose the IQNstring first 

160 for key in dict.keys(): 

161 IQNstring += "%s|" % key 

162 # Reinitialize and store iterator 

163 key_iterator = iter(dict.keys()) 

164 else: 

165 key_iterator = IQNs 

166 

167 # Now load the individual iSCSI base classes 

168 for key in key_iterator: 

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

170 srcmd_copy = copy.deepcopy(self.original_srcmd) 

171 srcmd_copy.dconf['target'] = ipaddr 

172 srcmd_copy.dconf['targetIQN'] = iqn 

173 srcmd_copy.dconf['multiSession'] = IQNstring 

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

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

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

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

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

179 dconf['multiSession'] = IQNstring 

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

181 except Exception as exc: 

182 util.logException("LVMoISCSISR.load") 

183 saved_exc = exc 

184 try: 

185 self.iscsi = self.iscsiSRs[0] 

186 except IndexError as exc: 

187 if isinstance(saved_exc, xs_errors.SROSError): 

188 raise saved_exc # pylint: disable-msg=E0702 

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

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

191 else: 

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

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

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

195 pbd = None 

196 try: 

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

198 except: 

199 pass 

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

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

202 # Dual controller issue 

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

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

205 self.iscsi = self.iscsiSRs[ii] 

206 self._LUNprint(sr_uuid) 

207 for key in self.iscsi.LUNs: 

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

209 self.print_LUNs_XML() 

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

211 raise xs_errors.XenError('ConfigSCSIid') 

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

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

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

215 # than one controller is present 

216 dev_match = False 

217 forced_login = False 

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

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

220 pass 

221 else: 

222 target_success = False 

223 attempt_discovery = False 

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

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

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

227 if forced_login == True: 

228 try: 

229 iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN) 

230 iscsilib.logout(self.iscsi.target, self.iscsi.targetIQN) 

231 forced_login = False 

232 except: 

233 raise xs_errors.XenError('ISCSILogout') 

234 self.iscsi = self.iscsiSRs[iii] 

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

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

237 iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN) 

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

239 attempt_discovery = True 

240 try: 

241 # Ensure iscsi db has been populated 

242 map = iscsilib.discovery( 

243 self.iscsi.target, 

244 self.iscsi.port, 

245 self.iscsi.chapuser, 

246 self.iscsi.chappassword, 

247 targetIQN=self.iscsi.targetIQN) 

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

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

250 " portal %s returned empty list" 

251 " Trying another path if available" % 

252 (self.iscsi.targetIQN, 

253 self.iscsi.target)) 

254 continue 

255 except: 

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

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

258 " available" % 

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

260 continue 

261 try: 

262 iscsilib.login(self.iscsi.target, 

263 self.iscsi.targetIQN, 

264 self.iscsi.chapuser, 

265 self.iscsi.chappassword, 

266 self.iscsi.incoming_chapuser, 

267 self.iscsi.incoming_chappassword, 

268 self.mpath == "true") 

269 except: 

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

271 " portal %s. Trying another path" 

272 " if available" % 

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

274 continue 

275 target_success = True 

276 forced_login = True 

277 # A session should be active. 

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

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

280 continue 

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

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

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

284 continue 

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

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

287 lun_dev = scsiutil.getdev(lun_path) 

288 try: 

289 lun_scsiid = scsiutil.getSCSIid(lun_dev) 

290 except: 

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

292 " offline or iscsi path down" % 

293 (lun_dev, self.iscsi.path)) 

294 continue 

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

296 if lun_scsiid == self.SCSIid: 

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

298 dev_match = True 

299 # No more need to raise ISCSITarget exception. 

300 # Resetting attempt_discovery 

301 attempt_discovery = False 

302 break 

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

304 if iii == 0: 

305 break 

306 util.SMlog("IQN reordering needed") 

307 new_iscsiSRs = [] 

308 IQNs = {} 

309 IQNstring = "" 

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

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

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

313 ipaddr = self.iscsiSRs[kkk].target 

314 port = self.iscsiSRs[kkk].port 

315 iqn = self.iscsiSRs[kkk].targetIQN 

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

317 # The final string must preserve the order without repetition 

318 if key not in IQNs: 

319 IQNs[key] = "" 

320 IQNstring += "%s|" % key 

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

322 self.iscsiSRs = new_iscsiSRs 

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

324 # Updating pbd entry, if any 

325 try: 

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

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

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

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

330 dconf['multiSession'] = IQNstring 

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

332 except: 

333 pass 

334 break 

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

336 raise xs_errors.XenError('ISCSITarget') 

337 

338 # Check for any unneeded open iscsi sessions 

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

340 try: 

341 iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN) 

342 iscsilib.logout(self.iscsi.target, self.iscsi.targetIQN) 

343 forced_login = False 

344 except: 

345 raise xs_errors.XenError('ISCSILogout') 

346 

347 def print_LUNs_XML(self): 

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

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

350 dom.appendChild(element) 

351 for uuid in self.LUNs: 

352 val = self.LUNs[uuid] 

353 entry = dom.createElement('LUN') 

354 element.appendChild(entry) 

355 

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

357 'size', 'SCSIid'): 

358 try: 

359 aval = getattr(val, attr) 

360 except AttributeError: 

361 continue 

362 

363 if aval: 

364 subentry = dom.createElement(attr) 

365 entry.appendChild(subentry) 

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

367 subentry.appendChild(textnode) 

368 

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

370 

371 def _getSCSIid_from_LUN(self, sr_uuid): 

372 was_attached = True 

373 self.iscsi.attach(sr_uuid) 

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

375 if len(dev) > 1: 

376 raise xs_errors.XenError('LVMOneLUN') 

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

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

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

380 try: 

381 SCSIid = scsiutil.getSCSIid(path) 

382 except: 

383 raise xs_errors.XenError('InvalidDev') 

384 self.iscsi.detach(sr_uuid) 

385 return SCSIid 

386 

387 def _LUNprint(self, sr_uuid): 

388 if self.iscsi.attached: 

389 # Force a rescan on the bus. 

390 self.iscsi.refresh() 

391# time.sleep(5) 

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

393 self.iscsi.attach(sr_uuid) 

394 

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

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

397 try: 

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

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

400 

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

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

403 sgdevs = [] 

404 for i in devs: 

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

406 sgdevs.sort() 

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

408 

409 # issue a report luns: 

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

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

412 

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

414 for i in range(0, 30): 

415 luns = scsiutil._dosgscan() 

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

417 if len(sgdevs) >= nluns: 

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

419 break 

420 else: 

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

422 time.sleep(1) 

423 

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

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

426 else: 

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

428 except: 

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

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

431 

432 self.iscsi.print_LUNs() 

433 self.iscsi.detach(sr_uuid) 

434 

435 @override 

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

437 # Check SCSIid not already in use by other PBDs 

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

439 raise xs_errors.XenError('SRInUse') 

440 

441 self.iscsi.attach(sr_uuid) 

442 try: 

443 self.iscsi._attach_LUN_bySCSIid(self.SCSIid) 

444 self._pathrefresh(LVMoISCSISR) 

445 LVMSR.LVMSR.create(self, sr_uuid, size) 

446 except Exception as inst: 

447 self.iscsi.detach(sr_uuid) 

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

449 self.iscsi.detach(sr_uuid) 

450 

451 @override 

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

453 self._pathrefresh(LVMoISCSISR) 

454 LVMSR.LVMSR.delete(self, sr_uuid) 

455 for i in self.iscsiSRs: 

456 i.detach(sr_uuid) 

457 

458 @override 

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

460 try: 

461 connected = False 

462 stored_exception = None 

463 for i in self.iscsiSRs: 

464 try: 

465 i.attach(sr_uuid) 

466 except xs_errors.SROSError as inst: 

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

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

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

470 stored_exception = inst 

471 continue 

472 else: 

473 raise 

474 else: 

475 connected = True 

476 

477 i._attach_LUN_bySCSIid(self.SCSIid) 

478 

479 # Check if at least one iscsi succeeded 

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

481 # pylint: disable=raising-bad-type 

482 raise stored_exception 

483 

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

485 # Force a manual bus refresh 

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

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

488 

489 self._pathrefresh(LVMoISCSISR) 

490 

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

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

493 

494 LVMSR.LVMSR.attach(self, sr_uuid) 

495 except Exception as inst: 

496 for i in self.iscsiSRs: 

497 i.detach(sr_uuid) 

498 

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

500 if isinstance(inst, xs_errors.SROSError): 

501 raise 

502 

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

504 

505 self._setMultipathableFlag(SCSIid=self.SCSIid) 

506 

507 @override 

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

509 LVMSR.LVMSR.detach(self, sr_uuid) 

510 for i in self.iscsiSRs: 

511 i.detach(sr_uuid) 

512 

513 @override 

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

515 self._pathrefresh(LVMoISCSISR) 

516 if self.mpath == "true": 

517 for i in self.iscsiSRs: 

518 try: 

519 i.attach(sr_uuid) 

520 except xs_errors.SROSError: 

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

522 LVMSR.LVMSR.scan(self, sr_uuid) 

523 

524 @override 

525 def probe(self) -> str: 

526 self.uuid = util.gen_uuid() 

527 

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

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

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

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

532 maps = [] 

533 try: 

534 maps = mpath_cli.list_maps() 

535 except: 

536 pass 

537 

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

539 raise xs_errors.XenError('SRInUse') 

540 

541 self.iscsi.attach(self.uuid) 

542 self.iscsi._attach_LUN_bySCSIid(self.SCSIid) 

543 self._pathrefresh(LVMoISCSISR) 

544 out = LVMSR.LVMSR.probe(self) 

545 self.iscsi.detach(self.uuid) 

546 return out 

547 

548 @override 

549 def check_sr(self, sr_uuid) -> None: 

550 """Hook to check SR health""" 

551 with iscsi_try_lock() as acquired: 

552 if not acquired: 552 ↛ 553line 552 didn't jump to line 553, because the condition on line 552 was never true

553 util.SMlog("check_sr: iSCSI operation in progress, skipping") 

554 return 

555 pbd_ref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

556 if not pbd_ref: 

557 return 

558 other_config = self.session.xenapi.PBD.get_other_config(pbd_ref) 

559 if not util.sessions_less_than_targets(other_config, self.dconf): 

560 return 

561 self.create_iscsi_sessions(sr_uuid) 

562 for iscsi in self.iscsiSRs: 

563 try: 

564 iscsi.attach(sr_uuid) 

565 except xs_errors.SROSError: 

566 util.SMlog("check_sr: Failed to attach iSCSI target") 

567 

568 @override 

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

570 return LVMoISCSIVDI(self, uuid) 

571 

572 

573class LVMoISCSIVDI(LVMSR.LVMVDI): 

574 @override 

575 def generate_config(self, sr_uuid, vdi_uuid) -> str: 

576 util.SMlog("LVMoISCSIVDI.generate_config") 

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

578 raise xs_errors.XenError('VDIUnavailable') 

579 dict = {} 

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

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

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

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

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

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

586 del dict['device_config']['chappassword_secret'] 

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

588 dict['sr_uuid'] = sr_uuid 

589 dict['vdi_uuid'] = vdi_uuid 

590 dict['command'] = 'vdi_attach_from_config' 

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

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

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

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

595 

596 @override 

597 def attach_from_config(self, sr_uuid, vdi_uuid) -> str: 

598 util.SMlog("LVMoISCSIVDI.attach_from_config") 

599 try: 

600 self.sr.iscsi.attach(sr_uuid) 

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

602 return LVMSR.LVMVDI.attach(self, sr_uuid, vdi_uuid) 

603 except: 

604 util.logException("LVMoISCSIVDI.attach_from_config") 

605 raise xs_errors.XenError('SRUnavailable', \ 

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

607 

608 

609if __name__ == '__main__': 609 ↛ 610line 609 didn't jump to line 610, because the condition on line 609 was never true

610 SRCommand.run(LVMoISCSISR, DRIVER_INFO) 

611else: 

612 SR.registerSR(LVMoISCSISR)