Coverage for drivers/LVHDoISCSISR.py : 38%

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#
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
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"]
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)']]
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 }
71class LVHDoISCSISR(LVHDSR.LVHDSR):
72 """LVHD over ISCSI storage repository"""
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)
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()
91 # If this is a vdi command, don't initialise SR
92 if util.isVDICommand(self.original_srcmd.cmd):
93 self.SCSIid = self.dconf['SCSIid']
94 else:
95 self.create_iscsi_sessions(sr_uuid)
97 LVHDSR.LVHDSR.load(self, sr_uuid)
99 def create_iscsi_sessions(self, sr_uuid):
100 if 'target' in self.original_srcmd.dconf: 100 ↛ 102line 100 didn't jump to line 102, because the condition on line 100 was never false
101 self.original_srcmd.dconf['targetlist'] = self.original_srcmd.dconf['target']
102 iscsi = BaseISCSI.BaseISCSISR(self.original_srcmd, sr_uuid)
103 self.iscsiSRs = []
104 self.iscsiSRs.append(iscsi)
105 saved_exc = None
106 targets = self.dconf['target'].split(',')
107 if len(targets) > 1 or self.dconf['targetIQN'] == "*":
108 # Instantiate multiple sessions
109 self.iscsiSRs = []
110 if self.dconf['targetIQN'] == "*": 110 ↛ 113line 110 didn't jump to line 113, because the condition on line 110 was never false
111 IQN = "any"
112 else:
113 IQN = self.dconf['targetIQN']
114 dict = {}
115 IQNstring = ""
116 IQNs = []
117 try:
118 if 'multiSession' in self.dconf:
119 IQNs = self.dconf['multiSession'].split("|")
120 for IQN in IQNs:
121 if IQN:
122 dict[IQN] = ""
123 else:
124 try:
125 IQNs.remove(IQN)
126 except:
127 # Exceptions are not expected but just in case
128 pass
129 # Order in multiSession must be preserved. It is important for dual-controllers.
130 # IQNstring cannot be built with a dictionary iteration because of this
131 IQNstring = self.dconf['multiSession']
132 else:
133 for tgt in targets: 133 ↛ 152line 133 didn't jump to line 152, because the loop on line 133 didn't complete
134 try:
135 tgt_ip = util._convertDNS(tgt)
136 except:
137 raise xs_errors.XenError('DNSError')
138 iscsilib.ensure_daemon_running_ok(iscsi.localIQN)
139 map = iscsilib.discovery(tgt_ip, iscsi.port, iscsi.chapuser, iscsi.chappassword, targetIQN=IQN)
140 util.SMlog("Discovery for IP %s returned %s" % (tgt, map))
141 for i in range(0, len(map)):
142 (portal, tpgt, iqn) = map[i]
143 (ipaddr, port) = iscsilib.parse_IP_port(portal)
144 try:
145 util._testHost(ipaddr, int(port), 'ISCSITarget')
146 except:
147 util.SMlog("Target Not reachable: (%s:%s)" % (ipaddr, port))
148 continue
149 key = "%s,%s,%s" % (ipaddr, port, iqn)
150 dict[key] = ""
151 # Again, do not mess up with IQNs order. Dual controllers will benefit from that
152 if IQNstring == "": 152 ↛ 154line 152 didn't jump to line 154, because the condition on line 152 was never true
153 # Compose the IQNstring first
154 for key in dict.keys():
155 IQNstring += "%s|" % key
156 # Reinitialize and store iterator
157 key_iterator = iter(dict.keys())
158 else:
159 key_iterator = IQNs
161 # Now load the individual iSCSI base classes
162 for key in key_iterator:
163 (ipaddr, port, iqn) = key.split(',')
164 srcmd_copy = copy.deepcopy(self.original_srcmd)
165 srcmd_copy.dconf['target'] = ipaddr
166 srcmd_copy.dconf['targetIQN'] = iqn
167 srcmd_copy.dconf['multiSession'] = IQNstring
168 util.SMlog("Setting targetlist: %s" % srcmd_copy.dconf['targetlist'])
169 self.iscsiSRs.append(BaseISCSI.BaseISCSISR(srcmd_copy, sr_uuid))
170 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
171 if pbd is not None and 'multiSession' not in self.dconf: 171 ↛ 172line 171 didn't jump to line 172, because the condition on line 171 was never true
172 dconf = self.session.xenapi.PBD.get_device_config(pbd)
173 dconf['multiSession'] = IQNstring
174 self.session.xenapi.PBD.set_device_config(pbd, dconf)
175 except Exception as exc:
176 util.logException("LVHDoISCSISR.load")
177 saved_exc = exc
178 try:
179 self.iscsi = self.iscsiSRs[0]
180 except IndexError as exc:
181 if isinstance(saved_exc, xs_errors.SROSError):
182 raise saved_exc # pylint: disable-msg=E0702
183 elif isinstance(saved_exc, Exception): 183 ↛ 186line 183 didn't jump to line 186, because the condition on line 183 was never false
184 raise xs_errors.XenError('SMGeneral', str(saved_exc))
185 else:
186 raise xs_errors.XenError('SMGeneral', str(exc))
187 # Be extremely careful not to throw exceptions here since this function
188 # is the main one used by all operations including probing and creating
189 pbd = None
190 try:
191 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
192 except:
193 pass
194 # Apart from the upgrade case, user must specify a SCSIid
195 if 'SCSIid' not in self.dconf: 195 ↛ 197line 195 didn't jump to line 197, because the condition on line 195 was never true
196 # Dual controller issue
197 self.LUNs = {} # Dict for LUNs from all the iscsi objects
198 for ii in range(0, len(self.iscsiSRs)):
199 self.iscsi = self.iscsiSRs[ii]
200 self._LUNprint(sr_uuid)
201 for key in self.iscsi.LUNs:
202 self.LUNs[key] = self.iscsi.LUNs[key]
203 self.print_LUNs_XML()
204 self.iscsi = self.iscsiSRs[0] # back to original value
205 raise xs_errors.XenError('ConfigSCSIid')
206 self.SCSIid = self.dconf['SCSIid']
207 # This block checks if the first iscsi target contains the right SCSIid.
208 # If not it scans the other iscsi targets because chances are that more
209 # than one controller is present
210 dev_match = False
211 forced_login = False
212 # No need to check if only one iscsi target is present
213 if len(self.iscsiSRs) == 1:
214 pass
215 else:
216 target_success = False
217 attempt_discovery = False
218 for iii in range(0, len(self.iscsiSRs)):
219 # Check we didn't leave any iscsi session open
220 # If exceptions happened before, the cleanup function has worked on the right target.
221 if forced_login == True:
222 try:
223 iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN)
224 iscsilib.logout(self.iscsi.target, self.iscsi.targetIQN)
225 forced_login = False
226 except:
227 raise xs_errors.XenError('ISCSILogout')
228 self.iscsi = self.iscsiSRs[iii]
229 util.SMlog("path %s" % self.iscsi.path)
230 util.SMlog("iscsci data: targetIQN %s, portal %s" % (self.iscsi.targetIQN, self.iscsi.target))
231 iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN)
232 if not iscsilib._checkTGT(self.iscsi.targetIQN, self.iscsi.target): 232 ↛ 272line 232 didn't jump to line 272, because the condition on line 232 was never false
233 attempt_discovery = True
234 try:
235 # Ensure iscsi db has been populated
236 map = iscsilib.discovery(
237 self.iscsi.target,
238 self.iscsi.port,
239 self.iscsi.chapuser,
240 self.iscsi.chappassword,
241 targetIQN=self.iscsi.targetIQN)
242 if len(map) == 0: 242 ↛ 243line 242 didn't jump to line 243, because the condition on line 242 was never true
243 util.SMlog("Discovery for iscsi data targetIQN %s,"
244 " portal %s returned empty list"
245 " Trying another path if available" %
246 (self.iscsi.targetIQN,
247 self.iscsi.target))
248 continue
249 except:
250 util.SMlog("Discovery failed for iscsi data targetIQN"
251 " %s, portal %s. Trying another path if"
252 " available" %
253 (self.iscsi.targetIQN, self.iscsi.target))
254 continue
255 try:
256 iscsilib.login(self.iscsi.target,
257 self.iscsi.targetIQN,
258 self.iscsi.chapuser,
259 self.iscsi.chappassword,
260 self.iscsi.incoming_chapuser,
261 self.iscsi.incoming_chappassword,
262 self.mpath == "true")
263 except:
264 util.SMlog("Login failed for iscsi data targetIQN %s,"
265 " portal %s. Trying another path"
266 " if available" %
267 (self.iscsi.targetIQN, self.iscsi.target))
268 continue
269 target_success = True
270 forced_login = True
271 # A session should be active.
272 if not util.wait_for_path(self.iscsi.path, BaseISCSI.MAX_TIMEOUT): 272 ↛ 273line 272 didn't jump to line 273, because the condition on line 272 was never true
273 util.SMlog("%s has no associated LUNs" % self.iscsi.targetIQN)
274 continue
275 scsiid_path = "/dev/disk/by-id/scsi-" + self.SCSIid
276 if not util.wait_for_path(scsiid_path, BaseISCSI.MAX_TIMEOUT): 276 ↛ 277line 276 didn't jump to line 277, because the condition on line 276 was never true
277 util.SMlog("%s not found" % scsiid_path)
278 continue
279 for file in filter(self.iscsi.match_lun, util.listdir(self.iscsi.path)): 279 ↛ 280line 279 didn't jump to line 280, because the loop on line 279 never started
280 lun_path = os.path.join(self.iscsi.path, file)
281 lun_dev = scsiutil.getdev(lun_path)
282 try:
283 lun_scsiid = scsiutil.getSCSIid(lun_dev)
284 except:
285 util.SMlog("getSCSIid failed on %s in iscsi %s: LUN"
286 " offline or iscsi path down" %
287 (lun_dev, self.iscsi.path))
288 continue
289 util.SMlog("dev from lun %s %s" % (lun_dev, lun_scsiid))
290 if lun_scsiid == self.SCSIid:
291 util.SMlog("lun match in %s" % self.iscsi.path)
292 dev_match = True
293 # No more need to raise ISCSITarget exception.
294 # Resetting attempt_discovery
295 attempt_discovery = False
296 break
297 if dev_match: 297 ↛ 298line 297 didn't jump to line 298, because the condition on line 297 was never true
298 if iii == 0:
299 break
300 util.SMlog("IQN reordering needed")
301 new_iscsiSRs = []
302 IQNs = {}
303 IQNstring = ""
304 # iscsiSRs can be seen as a circular buffer: the head now is the matching one
305 for kkk in list(range(iii, len(self.iscsiSRs))) + list(range(0, iii)):
306 new_iscsiSRs.append(self.iscsiSRs[kkk])
307 ipaddr = self.iscsiSRs[kkk].target
308 port = self.iscsiSRs[kkk].port
309 iqn = self.iscsiSRs[kkk].targetIQN
310 key = "%s,%s,%s" % (ipaddr, port, iqn)
311 # The final string must preserve the order without repetition
312 if key not in IQNs:
313 IQNs[key] = ""
314 IQNstring += "%s|" % key
315 util.SMlog("IQNstring is now %s" % IQNstring)
316 self.iscsiSRs = new_iscsiSRs
317 util.SMlog("iqn %s is leading now" % self.iscsiSRs[0].targetIQN)
318 # Updating pbd entry, if any
319 try:
320 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
321 if pbd is not None and 'multiSession' in self.dconf:
322 util.SMlog("Updating multiSession in PBD")
323 dconf = self.session.xenapi.PBD.get_device_config(pbd)
324 dconf['multiSession'] = IQNstring
325 self.session.xenapi.PBD.set_device_config(pbd, dconf)
326 except:
327 pass
328 break
329 if not target_success and attempt_discovery: 329 ↛ 330line 329 didn't jump to line 330, because the condition on line 329 was never true
330 raise xs_errors.XenError('ISCSITarget')
332 # Check for any unneeded open iscsi sessions
333 if forced_login == True: 333 ↛ exitline 333 didn't return from function 'create_iscsi_sessions', because the condition on line 333 was never false
334 try:
335 iscsilib.ensure_daemon_running_ok(self.iscsi.localIQN)
336 iscsilib.logout(self.iscsi.target, self.iscsi.targetIQN)
337 forced_login = False
338 except:
339 raise xs_errors.XenError('ISCSILogout')
341 def print_LUNs_XML(self):
342 dom = xml.dom.minidom.Document()
343 element = dom.createElement("iscsi-target")
344 dom.appendChild(element)
345 for uuid in self.LUNs:
346 val = self.LUNs[uuid]
347 entry = dom.createElement('LUN')
348 element.appendChild(entry)
350 for attr in ('vendor', 'serial', 'LUNid', \
351 'size', 'SCSIid'):
352 try:
353 aval = getattr(val, attr)
354 except AttributeError:
355 continue
357 if aval:
358 subentry = dom.createElement(attr)
359 entry.appendChild(subentry)
360 textnode = dom.createTextNode(str(aval))
361 subentry.appendChild(textnode)
363 print(dom.toprettyxml(), file=sys.stderr)
365 def _getSCSIid_from_LUN(self, sr_uuid):
366 was_attached = True
367 self.iscsi.attach(sr_uuid)
368 dev = self.dconf['LUNid'].split(',')
369 if len(dev) > 1:
370 raise xs_errors.XenError('LVMOneLUN')
371 path = os.path.join(self.iscsi.path, "LUN%s" % dev[0])
372 if not util.wait_for_path(path, BaseISCSI.MAX_TIMEOUT):
373 util.SMlog("Unable to detect LUN attached to host [%s]" % path)
374 try:
375 SCSIid = scsiutil.getSCSIid(path)
376 except:
377 raise xs_errors.XenError('InvalidDev')
378 self.iscsi.detach(sr_uuid)
379 return SCSIid
381 def _LUNprint(self, sr_uuid):
382 if self.iscsi.attached:
383 # Force a rescan on the bus.
384 self.iscsi.refresh()
385# time.sleep(5)
386# Now call attach (handles the refcounting + session activa)
387 self.iscsi.attach(sr_uuid)
389 util.SMlog("LUNprint: waiting for path: %s" % self.iscsi.path)
390 if util.wait_for_path("%s/LUN*" % self.iscsi.path, BaseISCSI.MAX_TIMEOUT):
391 try:
392 adapter = self.iscsi.adapter[self.iscsi.address]
393 util.SMlog("adapter=%s" % adapter)
395 # find a scsi device on which to issue a report luns command:
396 devs = glob.glob("%s/LUN*" % self.iscsi.path)
397 sgdevs = []
398 for i in devs:
399 sgdevs.append(int(i.split("LUN")[1]))
400 sgdevs.sort()
401 sgdev = "%s/LUN%d" % (self.iscsi.path, sgdevs[0])
403 # issue a report luns:
404 luns = util.pread2(["/usr/bin/sg_luns", "-q", sgdev]).split('\n')
405 nluns = len(luns) - 1 # remove the line relating to the final \n
407 # make sure we've got that many sg devices present
408 for i in range(0, 30):
409 luns = scsiutil._dosgscan()
410 sgdevs = [r for r in luns if r[1] == adapter]
411 if len(sgdevs) >= nluns:
412 util.SMlog("Got all %d sg devices" % nluns)
413 break
414 else:
415 util.SMlog("Got %d sg devices - expecting %d" % (len(sgdevs), nluns))
416 time.sleep(1)
418 if os.path.exists("/sbin/udevsettle"):
419 util.pread2(["/sbin/udevsettle"])
420 else:
421 util.pread2(["/sbin/udevadm", "settle"])
422 except:
423 util.SMlog("Generic exception caught. Pass")
424 pass # Make sure we don't break the probe...
426 self.iscsi.print_LUNs()
427 self.iscsi.detach(sr_uuid)
429 def create(self, sr_uuid, size):
430 # Check SCSIid not already in use by other PBDs
431 if util.test_SCSIid(self.session, sr_uuid, self.SCSIid):
432 raise xs_errors.XenError('SRInUse')
434 self.iscsi.attach(sr_uuid)
435 try:
436 self.iscsi._attach_LUN_bySCSIid(self.SCSIid)
437 self._pathrefresh(LVHDoISCSISR)
438 LVHDSR.LVHDSR.create(self, sr_uuid, size)
439 except Exception as inst:
440 self.iscsi.detach(sr_uuid)
441 raise xs_errors.XenError("SRUnavailable", opterr=inst)
442 self.iscsi.detach(sr_uuid)
444 def delete(self, sr_uuid):
445 self._pathrefresh(LVHDoISCSISR)
446 LVHDSR.LVHDSR.delete(self, sr_uuid)
447 for i in self.iscsiSRs:
448 i.detach(sr_uuid)
450 def attach(self, sr_uuid):
451 try:
452 connected = False
453 stored_exception = None
454 for i in self.iscsiSRs:
455 try:
456 i.attach(sr_uuid)
457 except xs_errors.SROSError as inst:
458 # Some iscsi objects can fail login/discovery but not all. Storing exception
459 if inst.errno in [141, 83]:
460 util.SMlog("Connection failed for target %s, continuing.." % i.target)
461 stored_exception = inst
462 continue
463 else:
464 raise
465 else:
466 connected = True
468 i._attach_LUN_bySCSIid(self.SCSIid)
470 # Check if at least one iscsi succeeded
471 if not connected and stored_exception: 471 ↛ 473line 471 didn't jump to line 473, because the condition on line 471 was never true
472 # pylint: disable=raising-bad-type
473 raise stored_exception
475 if 'multiSession' in self.dconf: 475 ↛ 480line 475 didn't jump to line 480, because the condition on line 475 was never false
476 # Force a manual bus refresh
477 for a in self.iscsi.adapter: 477 ↛ 478line 477 didn't jump to line 478, because the loop on line 477 never started
478 scsiutil.rescan([self.iscsi.adapter[a]])
480 self._pathrefresh(LVHDoISCSISR)
481 LVHDSR.LVHDSR.attach(self, sr_uuid)
482 except Exception as inst:
483 for i in self.iscsiSRs:
484 i.detach(sr_uuid)
485 raise xs_errors.XenError("SRUnavailable", opterr=inst)
486 self._setMultipathableFlag(SCSIid=self.SCSIid)
488 def detach(self, sr_uuid):
489 LVHDSR.LVHDSR.detach(self, sr_uuid)
490 for i in self.iscsiSRs:
491 i.detach(sr_uuid)
493 def scan(self, sr_uuid):
494 self._pathrefresh(LVHDoISCSISR)
495 if self.mpath == "true":
496 for i in self.iscsiSRs:
497 try:
498 i.attach(sr_uuid)
499 except xs_errors.SROSError:
500 util.SMlog("Connection failed for target %s, continuing.." % i.target)
501 LVHDSR.LVHDSR.scan(self, sr_uuid)
503 def probe(self):
504 self.uuid = util.gen_uuid()
506 # When multipathing is enabled, since we don't refcount the multipath maps,
507 # we should not attempt to do the iscsi.attach/detach when the map is already present,
508 # as this will remove it (which may well be in use).
509 if self.mpath == 'true' and 'SCSIid' in self.dconf:
510 maps = []
511 try:
512 maps = mpath_cli.list_maps()
513 except:
514 pass
516 if self.dconf['SCSIid'] in maps:
517 raise xs_errors.XenError('SRInUse')
519 self.iscsi.attach(self.uuid)
520 self.iscsi._attach_LUN_bySCSIid(self.SCSIid)
521 self._pathrefresh(LVHDoISCSISR)
522 out = LVHDSR.LVHDSR.probe(self)
523 self.iscsi.detach(self.uuid)
524 return out
526 def check_sr(self, sr_uuid):
527 """Hook to check SR health"""
528 pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
529 if pbdref:
530 other_config = self.session.xenapi.PBD.get_other_config(pbdref)
531 if util.sessions_less_than_targets(other_config, self.dconf):
532 self.create_iscsi_sessions(sr_uuid)
533 for iscsi in self.iscsiSRs:
534 try:
535 iscsi.attach(sr_uuid)
536 except xs_errors.SROSError:
537 util.SMlog("Failed to attach iSCSI target")
539 def vdi(self, uuid):
540 return LVHDoISCSIVDI(self, uuid)
543class LVHDoISCSIVDI(LVHDSR.LVHDVDI):
544 def generate_config(self, sr_uuid, vdi_uuid):
545 util.SMlog("LVHDoISCSIVDI.generate_config")
546 if not lvutil._checkLV(self.path):
547 raise xs_errors.XenError('VDIUnavailable')
548 dict = {}
549 self.sr.dconf['localIQN'] = self.sr.iscsi.localIQN
550 self.sr.dconf['multipathing'] = self.sr.mpath
551 self.sr.dconf['multipathhandle'] = self.sr.mpathhandle
552 dict['device_config'] = self.sr.dconf
553 if 'chappassword_secret' in dict['device_config']:
554 s = util.get_secret(self.session, dict['device_config']['chappassword_secret'])
555 del dict['device_config']['chappassword_secret']
556 dict['device_config']['chappassword'] = s
557 dict['sr_uuid'] = sr_uuid
558 dict['vdi_uuid'] = vdi_uuid
559 dict['command'] = 'vdi_attach_from_config'
560 # Return the 'config' encoded within a normal XMLRPC response so that
561 # we can use the regular response/error parsing code.
562 config = xmlrpc.client.dumps(tuple([dict]), "vdi_attach_from_config")
563 return xmlrpc.client.dumps((config, ), "", True)
565 def attach_from_config(self, sr_uuid, vdi_uuid):
566 util.SMlog("LVHDoISCSIVDI.attach_from_config")
567 try:
568 self.sr.iscsi.attach(sr_uuid)
569 self.sr.iscsi._attach_LUN_bySCSIid(self.sr.SCSIid)
570 return LVHDSR.LVHDVDI.attach(self, sr_uuid, vdi_uuid)
571 except:
572 util.logException("LVHDoISCSIVDI.attach_from_config")
573 raise xs_errors.XenError('SRUnavailable', \
574 opterr='Unable to attach the heartbeat disk')
577if __name__ == '__main__': 577 ↛ 578line 577 didn't jump to line 578, because the condition on line 577 was never true
578 SRCommand.run(LVHDoISCSISR, DRIVER_INFO)
579else:
580 SR.registerSR(LVHDoISCSISR)