Coverage for drivers/LVHDoISCSISR.py : 40%

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