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
« 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#
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
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"]
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)']]
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 }
68class LVHDoISCSISR(LVHDSR.LVHDSR):
69 """LVHD over ISCSI storage repository"""
71 @staticmethod
72 def handles(type):
73 if type == "lvmoiscsi":
74 return True
75 if type == "lvhdoiscsi":
76 return True
77 return False
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()
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)
90 LVHDSR.LVHDSR.load(self, sr_uuid)
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
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')
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')
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)
343 for attr in ('vendor', 'serial', 'LUNid', \
344 'size', 'SCSIid'):
345 try:
346 aval = getattr(val, attr)
347 except AttributeError:
348 continue
350 if aval:
351 subentry = dom.createElement(attr)
352 entry.appendChild(subentry)
353 textnode = dom.createTextNode(str(aval))
354 subentry.appendChild(textnode)
356 print(dom.toprettyxml(), file=sys.stderr)
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
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)
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)
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])
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
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)
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...
419 self.iscsi.print_LUNs()
420 self.iscsi.detach(sr_uuid)
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')
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)
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)
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
461 i._attach_LUN_bySCSIid(self.SCSIid)
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
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]])
473 self._pathrefresh(LVHDoISCSISR)
475 # Check that we only have PVs for the volume group with the expected SCSI ID
476 lvutil.checkPVScsiIds(self.vgname, self.SCSIid)
478 LVHDSR.LVHDSR.attach(self, sr_uuid)
479 except Exception as inst:
480 for i in self.iscsiSRs:
481 i.detach(sr_uuid)
483 # If we already have a proper error just raise it
484 if isinstance(inst, xs_errors.SROSError):
485 raise
487 raise xs_errors.XenError("SRUnavailable", opterr=inst)
489 self._setMultipathableFlag(SCSIid=self.SCSIid)
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)
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)
507 def probe(self):
508 self.uuid = util.gen_uuid()
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
520 if self.dconf['SCSIid'] in maps:
521 raise xs_errors.XenError('SRInUse')
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
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")
543 def vdi(self, uuid):
544 return LVHDoISCSIVDI(self, uuid)
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)
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')
580# SR registration at import
581SR.registerSR(LVHDoISCSISR)