Coverage for drivers/LVMoISCSISR.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# LVMoISCSISR: LVM over ISCSI software initiator SR driver
19#
21from sm_typing import override
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
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"]
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)']]
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 }
75class LVMoISCSISR(LVMSR.LVMSR):
76 """LVM over ISCSI storage repository"""
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
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()
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)
103 LVMSR.LVMSR.load(self, sr_uuid)
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
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')
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')
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)
356 for attr in ('vendor', 'serial', 'LUNid', \
357 'size', 'SCSIid'):
358 try:
359 aval = getattr(val, attr)
360 except AttributeError:
361 continue
363 if aval:
364 subentry = dom.createElement(attr)
365 entry.appendChild(subentry)
366 textnode = dom.createTextNode(str(aval))
367 subentry.appendChild(textnode)
369 print(dom.toprettyxml(), file=sys.stderr)
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
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)
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)
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])
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
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)
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...
432 self.iscsi.print_LUNs()
433 self.iscsi.detach(sr_uuid)
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')
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)
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)
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
477 i._attach_LUN_bySCSIid(self.SCSIid)
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
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]])
489 self._pathrefresh(LVMoISCSISR)
491 # Check that we only have PVs for the volume group with the expected SCSI ID
492 lvutil.checkPVScsiIds(self.vgname, self.SCSIid)
494 LVMSR.LVMSR.attach(self, sr_uuid)
495 except Exception as inst:
496 for i in self.iscsiSRs:
497 i.detach(sr_uuid)
499 # If we already have a proper error just raise it
500 if isinstance(inst, xs_errors.SROSError):
501 raise
503 raise xs_errors.XenError("SRUnavailable", opterr=inst)
505 self._setMultipathableFlag(SCSIid=self.SCSIid)
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)
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)
524 @override
525 def probe(self) -> str:
526 self.uuid = util.gen_uuid()
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
538 if self.dconf['SCSIid'] in maps:
539 raise xs_errors.XenError('SRInUse')
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
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")
568 @override
569 def vdi(self, uuid) -> VDI.VDI:
570 return LVMoISCSIVDI(self, uuid)
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)
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')
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)