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

19# 

20 

21import SR 

22import LVHDSR 

23import BaseISCSI 

24import SRCommand 

25import util 

26import scsiutil 

27import lvutil 

28import time 

29import os 

30import sys 

31import xs_errors 

32import xmlrpc.client 

33import mpath_cli 

34import iscsilib 

35import glob 

36import copy 

37import scsiutil 

38import xml.dom.minidom 

39 

40CAPABILITIES = ["SR_PROBE", "SR_UPDATE", "SR_METADATA", "SR_TRIM", 

41 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", 

42 "VDI_GENERATE_CONFIG", "VDI_CLONE", "VDI_SNAPSHOT", 

43 "VDI_RESIZE", "ATOMIC_PAUSE", "VDI_RESET_ON_BOOT/2", 

44 "VDI_UPDATE", "VDI_MIRROR", "VDI_CONFIG_CBT", 

45 "VDI_ACTIVATE", "VDI_DEACTIVATE"] 

46 

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

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

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

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

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

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

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

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

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

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

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

58 

59DRIVER_INFO = { 

60 'name': 'LVHD over iSCSI', 

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

62 'vendor': 'Citrix Systems Inc', 

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

64 'driver_version': '1.0', 

65 'required_api_version': '1.0', 

66 'capabilities': CAPABILITIES, 

67 'configuration': CONFIGURATION 

68 } 

69 

70 

71class LVHDoISCSISR(LVHDSR.LVHDSR): 

72 """LVHD over ISCSI storage repository""" 

73 

74 def handles(type): 

75 if __name__ == '__main__': 

76 name = sys.argv[0] 

77 else: 

78 name = __name__ 

79 if name.endswith("LVMoISCSISR"): 

80 return type == "lvmoiscsi" 

81 if type == "lvhdoiscsi": 

82 return True 

83 return False 

84 handles = staticmethod(handles) 

85 

86 def load(self, sr_uuid): 

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

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

89 sr_uuid = util.gen_uuid() 

90 

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

92 if util.isVDICommand(self.original_srcmd.cmd): 92 ↛ 93line 92 didn't jump to line 93, because the condition on line 92 was never true

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

94 else: 

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

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

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

98 self.iscsiSRs = [] 

99 self.iscsiSRs.append(iscsi) 

100 

101 saved_exc = None 

102 if self.dconf['target'].find(',') == 0 or self.dconf['targetIQN'] == "*": 102 ↛ 173line 102 didn't jump to line 173, because the condition on line 102 was never false

103 # Instantiate multiple sessions 

104 self.iscsiSRs = [] 

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

106 IQN = "any" 

107 else: 

108 IQN = self.dconf['targetIQN'] 

109 dict = {} 

110 IQNstring = "" 

111 IQNs = [] 

112 try: 

113 if 'multiSession' in self.dconf: 113 ↛ 114line 113 didn't jump to line 114, because the condition on line 113 was never true

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

115 for IQN in IQNs: 

116 if IQN: 

117 dict[IQN] = "" 

118 else: 

119 try: 

120 IQNs.remove(IQN) 

121 except: 

122 # Exceptions are not expected but just in case 

123 pass 

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

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

126 IQNstring = self.dconf['multiSession'] 

127 else: 

128 for tgt in self.dconf['target'].split(','): 128 ↛ 147line 128 didn't jump to line 147, because the loop on line 128 didn't complete

129 try: 

130 tgt_ip = util._convertDNS(tgt) 

131 except: 

132 raise xs_errors.XenError('DNSError') 

133 iscsilib.ensure_daemon_running_ok(iscsi.localIQN) 

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

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

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

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

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

139 try: 

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

141 except: 

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

143 continue 

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

145 dict[key] = "" 

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

147 if IQNstring == "": 

148 # Compose the IQNstring first 

149 for key in dict.keys(): 

150 IQNstring += "%s|" % key 

151 # Reinitialize and store iterator 

152 key_iterator = iter(dict.keys()) 

153 else: 

154 key_iterator = IQNs 

155 

156 # Now load the individual iSCSI base classes 

157 for key in key_iterator: 

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

159 srcmd_copy = copy.deepcopy(self.original_srcmd) 

160 srcmd_copy.dconf['target'] = ipaddr 

161 srcmd_copy.dconf['targetIQN'] = iqn 

162 srcmd_copy.dconf['multiSession'] = IQNstring 

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

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

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

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

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

168 dconf['multiSession'] = IQNstring 

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

170 except Exception as exc: 

171 util.logException("LVHDoISCSISR.load") 

172 saved_exc = exc 

173 try: 

174 self.iscsi = self.iscsiSRs[0] 

175 except IndexError as exc: 

176 if isinstance(saved_exc, xs_errors.SROSError): 

177 raise saved_exc # pylint: disable-msg=E0702 

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

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

180 else: 

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

182 

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

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

185 pbd = None 

186 try: 

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

188 except: 

189 pass 

190 

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

192 if 'SCSIid' not in self.dconf: 

193 # Dual controller issue 

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

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

196 self.iscsi = self.iscsiSRs[ii] 

197 self._LUNprint(sr_uuid) 

198 for key in self.iscsi.LUNs: 

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

200 self.print_LUNs_XML() 

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

202 raise xs_errors.XenError('ConfigSCSIid') 

203 

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

205 

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

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

208 # than one controller is present 

209 dev_match = False 

210 forced_login = False 

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

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

213 pass 

214 else: 

215 target_success = False 

216 attempt_discovery = False 

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

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

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

220 if forced_login == True: 

221 try: 

222 iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN) 

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

224 forced_login = False 

225 except: 

226 raise xs_errors.XenError('ISCSILogout') 

227 self.iscsi = self.iscsiSRs[iii] 

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

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

230 iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN) 

231 if not iscsilib._checkTGT(self.iscsi.targetIQN): 

232 attempt_discovery = True 

233 try: 

234 # Ensure iscsi db has been populated 

235 map = iscsilib.discovery( 

236 self.iscsi.target, 

237 self.iscsi.port, 

238 self.iscsi.chapuser, 

239 self.iscsi.chappassword, 

240 targetIQN=self.iscsi.targetIQN) 

241 if len(map) == 0: 

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

243 " portal %s returned empty list" 

244 " Trying another path if available" % 

245 (self.iscsi.targetIQN, 

246 self.iscsi.target)) 

247 continue 

248 except: 

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

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

251 " available" % 

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

253 continue 

254 try: 

255 iscsilib.login(self.iscsi.target, 

256 self.iscsi.targetIQN, 

257 self.iscsi.chapuser, 

258 self.iscsi.chappassword, 

259 self.iscsi.incoming_chapuser, 

260 self.iscsi.incoming_chappassword, 

261 self.mpath == "true") 

262 except: 

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

264 " portal %s. Trying another path" 

265 " if available" % 

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

267 continue 

268 target_success = True 

269 forced_login = True 

270 # A session should be active. 

271 if not util.wait_for_path(self.iscsi.path, BaseISCSI.MAX_TIMEOUT): 

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

273 continue 

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

275 if not util.wait_for_path(scsiid_path, BaseISCSI.MAX_TIMEOUT): 

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

277 continue 

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

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

280 lun_dev = scsiutil.getdev(lun_path) 

281 try: 

282 lun_scsiid = scsiutil.getSCSIid(lun_dev) 

283 except: 

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

285 " offline or iscsi path down" % 

286 (lun_dev, self.iscsi.path)) 

287 continue 

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

289 if lun_scsiid == self.SCSIid: 

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

291 dev_match = True 

292 # No more need to raise ISCSITarget exception. 

293 # Resetting attempt_discovery 

294 attempt_discovery = False 

295 break 

296 if dev_match: 

297 if iii == 0: 

298 break 

299 util.SMlog("IQN reordering needed") 

300 new_iscsiSRs = [] 

301 IQNs = {} 

302 IQNstring = "" 

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

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

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

306 ipaddr = self.iscsiSRs[kkk].target 

307 port = self.iscsiSRs[kkk].port 

308 iqn = self.iscsiSRs[kkk].targetIQN 

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

310 # The final string must preserve the order without repetition 

311 if key not in IQNs: 

312 IQNs[key] = "" 

313 IQNstring += "%s|" % key 

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

315 self.iscsiSRs = new_iscsiSRs 

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

317 # Updating pbd entry, if any 

318 try: 

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

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

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

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

323 dconf['multiSession'] = IQNstring 

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

325 except: 

326 pass 

327 break 

328 if not target_success and attempt_discovery: 

329 raise xs_errors.XenError('ISCSITarget') 

330 

331 LVHDSR.LVHDSR.load(self, sr_uuid) 

332 

333 def print_LUNs_XML(self): 

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

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

336 dom.appendChild(element) 

337 for uuid in self.LUNs: 

338 val = self.LUNs[uuid] 

339 entry = dom.createElement('LUN') 

340 element.appendChild(entry) 

341 

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

343 'size', 'SCSIid'): 

344 try: 

345 aval = getattr(val, attr) 

346 except AttributeError: 

347 continue 

348 

349 if aval: 

350 subentry = dom.createElement(attr) 

351 entry.appendChild(subentry) 

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

353 subentry.appendChild(textnode) 

354 

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

356 

357 def _getSCSIid_from_LUN(self, sr_uuid): 

358 was_attached = True 

359 self.iscsi.attach(sr_uuid) 

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

361 if len(dev) > 1: 

362 raise xs_errors.XenError('LVMOneLUN') 

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

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

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

366 try: 

367 SCSIid = scsiutil.getSCSIid(path) 

368 except: 

369 raise xs_errors.XenError('InvalidDev') 

370 self.iscsi.detach(sr_uuid) 

371 return SCSIid 

372 

373 def _LUNprint(self, sr_uuid): 

374 if self.iscsi.attached: 

375 # Force a rescan on the bus. 

376 self.iscsi.refresh() 

377# time.sleep(5) 

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

379 self.iscsi.attach(sr_uuid) 

380 

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

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

383 try: 

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

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

386 

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

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

389 sgdevs = [] 

390 for i in devs: 

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

392 sgdevs.sort() 

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

394 

395 # issue a report luns: 

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

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

398 

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

400 for i in range(0, 30): 

401 luns = scsiutil._dosgscan() 

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

403 if len(sgdevs) >= nluns: 

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

405 break 

406 else: 

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

408 time.sleep(1) 

409 

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

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

412 else: 

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

414 except: 

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

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

417 

418 self.iscsi.print_LUNs() 

419 self.iscsi.detach(sr_uuid) 

420 

421 def create(self, sr_uuid, size): 

422 # Check SCSIid not already in use by other PBDs 

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

424 raise xs_errors.XenError('SRInUse') 

425 

426 self.iscsi.attach(sr_uuid) 

427 try: 

428 self.iscsi._attach_LUN_bySCSIid(self.SCSIid) 

429 self._pathrefresh(LVHDoISCSISR) 

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

431 except Exception as inst: 

432 self.iscsi.detach(sr_uuid) 

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

434 self.iscsi.detach(sr_uuid) 

435 

436 def delete(self, sr_uuid): 

437 self._pathrefresh(LVHDoISCSISR) 

438 LVHDSR.LVHDSR.delete(self, sr_uuid) 

439 for i in self.iscsiSRs: 

440 i.detach(sr_uuid) 

441 

442 def attach(self, sr_uuid): 

443 try: 

444 connected = False 

445 stored_exception = None 

446 for i in self.iscsiSRs: 

447 try: 

448 i.attach(sr_uuid) 

449 except xs_errors.SROSError as inst: 

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

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

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

453 stored_exception = inst 

454 continue 

455 else: 

456 raise 

457 else: 

458 connected = True 

459 

460 i._attach_LUN_bySCSIid(self.SCSIid) 

461 

462 # Check if at least one iscsi succeeded 

463 if not connected and stored_exception: 

464 # pylint: disable=raising-bad-type 

465 raise stored_exception 

466 

467 if 'multiSession' in self.dconf: 

468 # Force a manual bus refresh 

469 for a in self.iscsi.adapter: 

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

471 

472 self._pathrefresh(LVHDoISCSISR) 

473 LVHDSR.LVHDSR.attach(self, sr_uuid) 

474 except Exception as inst: 

475 for i in self.iscsiSRs: 

476 i.detach(sr_uuid) 

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

478 self._setMultipathableFlag(SCSIid=self.SCSIid) 

479 

480 def detach(self, sr_uuid): 

481 LVHDSR.LVHDSR.detach(self, sr_uuid) 

482 for i in self.iscsiSRs: 

483 i.detach(sr_uuid) 

484 

485 def scan(self, sr_uuid): 

486 self._pathrefresh(LVHDoISCSISR) 

487 if self.mpath == "true": 

488 for i in self.iscsiSRs: 

489 try: 

490 i.attach(sr_uuid) 

491 except xs_errors.SROSError: 

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

493 LVHDSR.LVHDSR.scan(self, sr_uuid) 

494 

495 def probe(self): 

496 self.uuid = util.gen_uuid() 

497 

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

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

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

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

502 maps = [] 

503 try: 

504 maps = mpath_cli.list_maps() 

505 except: 

506 pass 

507 

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

509 raise xs_errors.XenError('SRInUse') 

510 

511 self.iscsi.attach(self.uuid) 

512 self.iscsi._attach_LUN_bySCSIid(self.SCSIid) 

513 self._pathrefresh(LVHDoISCSISR) 

514 out = LVHDSR.LVHDSR.probe(self) 

515 self.iscsi.detach(self.uuid) 

516 return out 

517 

518 def vdi(self, uuid): 

519 return LVHDoISCSIVDI(self, uuid) 

520 

521 

522class LVHDoISCSIVDI(LVHDSR.LVHDVDI): 

523 def generate_config(self, sr_uuid, vdi_uuid): 

524 util.SMlog("LVHDoISCSIVDI.generate_config") 

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

526 raise xs_errors.XenError('VDIUnavailable') 

527 dict = {} 

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

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

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

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

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

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

534 del dict['device_config']['chappassword_secret'] 

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

536 dict['sr_uuid'] = sr_uuid 

537 dict['vdi_uuid'] = vdi_uuid 

538 dict['command'] = 'vdi_attach_from_config' 

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

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

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

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

543 

544 def attach_from_config(self, sr_uuid, vdi_uuid): 

545 util.SMlog("LVHDoISCSIVDI.attach_from_config") 

546 try: 

547 self.sr.iscsi.attach(sr_uuid) 

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

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

550 except: 

551 util.logException("LVHDoISCSIVDI.attach_from_config") 

552 raise xs_errors.XenError('SRUnavailable', \ 

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

554 

555 

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

557 SRCommand.run(LVHDoISCSISR, DRIVER_INFO) 

558else: 

559 SR.registerSR(LVHDoISCSISR)