Coverage for drivers/BaseISCSI.py : 39%

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# ISCSISR: ISCSI software initiator SR driver
19#
21from sm_typing import override
23import SR
24import VDI
25import util
26import time
27import LUNperVDI
28import os
29import sys
30import re
31import glob
32import xml.dom.minidom
33import scsiutil
34import iscsilib
35import xs_errors
37INITIATORNAME_FILE = '/etc/iscsi/initiatorname.iscsi'
38SECTOR_SHIFT = 9
39DEFAULT_PORT = 3260
40# 2^16 Max port number value
41MAXPORT = 65535
42MAX_TIMEOUT = 15
43MAX_LUNID_TIMEOUT = 60
44ISCSI_PROCNAME = "iscsi_tcp"
47class BaseISCSISR(SR.SR):
48 """ISCSI storage repository"""
50 @property
51 def force_tapdisk(self):
52 return self.dconf.get('force_tapdisk', 'false') == 'true'
54 @property
55 def attached(self):
56 if not self._attached:
57 self._attached = False
58 self._attached = iscsilib._checkTGT(self.targetIQN, self.target)
59 return self._attached
61 @attached.setter
62 def attached(self, value):
63 self._attached = value
65 @property
66 def pathdict(self):
67 if not self._pathdict: 67 ↛ 68line 67 didn't jump to line 68, because the condition on line 67 was never true
68 self._initPaths()
69 return self._pathdict
71 @property
72 def adapter(self):
73 if not self._adapter: 73 ↛ 74line 73 didn't jump to line 74, because the condition on line 73 was never true
74 self._initPaths()
75 return self._adapter
77 @adapter.setter
78 def adapter(self, value):
79 self._adapter = value
81 @property
82 def devs(self):
83 if not self._devs:
84 self._initPaths()
85 return self._devs
87 @property
88 def tgtidx(self):
89 """This appears to only be referenced by a unit test. Do we really need it? """
90 if not self._tgtidx: 90 ↛ 91line 90 didn't jump to line 91, because the condition on line 90 was never true
91 self._initPaths()
92 return self._tgtidx
94 @property
95 def path(self):
96 if not self._path: 96 ↛ 97line 96 didn't jump to line 97, because the condition on line 96 was never true
97 self._initPaths()
98 return self._path
100 @property
101 def address(self):
102 if not self._address: 102 ↛ 103line 102 didn't jump to line 103, because the condition on line 102 was never true
103 self._initPaths()
104 return self._address
106 @override
107 @staticmethod
108 def handles(type) -> bool:
109 return False
111 def _synchroniseAddrList(self, addrlist) -> None:
112 if not self.multihomed: 112 ↛ 114line 112 didn't jump to line 114, because the condition on line 112 was never false
113 return
114 change = False
115 if 'multihomelist' not in self.dconf:
116 change = True
117 self.mlist = []
118 mstr = ""
119 else:
120 self.mlist = self.dconf['multihomelist'].split(',')
121 mstr = self.dconf['multihomelist']
122 for val in addrlist:
123 if not val in self.mlist:
124 self.mlist.append(val)
125 if len(mstr):
126 mstr += ","
127 mstr += val
128 change = True
129 if change:
130 pbd = None
131 try:
132 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
133 if pbd is not None:
134 device_config = self.session.xenapi.PBD.get_device_config(pbd)
135 device_config['multihomelist'] = mstr
136 self.session.xenapi.PBD.set_device_config(pbd, device_config)
137 except:
138 pass
140 @override
141 def load(self, sr_uuid) -> None:
142 if self.force_tapdisk:
143 self.sr_vditype = 'aio'
144 else:
145 self.sr_vditype = 'phy'
146 self.discoverentry = 0
147 self.default_vdi_visibility = False
149 # Required parameters
150 if 'target' not in self.dconf or not self.dconf['target']: 150 ↛ 151line 150 didn't jump to line 151, because the condition on line 150 was never true
151 raise xs_errors.XenError('ConfigTargetMissing')
153 # we are no longer putting hconf in the xml.
154 # Instead we pass a session and host ref and let the SM backend query XAPI itself
155 try:
156 if 'localIQN' not in self.dconf: 156 ↛ 157line 156 didn't jump to line 157, because the condition on line 156 was never true
157 self.localIQN = self.session.xenapi.host.get_other_config(self.host_ref)['iscsi_iqn']
158 else:
159 self.localIQN = self.dconf['localIQN']
160 except:
161 raise xs_errors.XenError('ConfigISCSIIQNMissing')
163 # Check for empty string
164 if not self.localIQN: 164 ↛ 165line 164 didn't jump to line 165, because the condition on line 164 was never true
165 raise xs_errors.XenError('ConfigISCSIIQNMissing')
167 try:
168 self.target = util._convertDNS(self.dconf['target'].split(',')[0])
169 except:
170 raise xs_errors.XenError('DNSError')
172 self.targetlist = self.target
173 if 'targetlist' in self.dconf: 173 ↛ 174line 173 didn't jump to line 174, because the condition on line 173 was never true
174 self.targetlist = self.dconf['targetlist']
176 # Optional parameters
177 self.chapuser = ""
178 self.chappassword = ""
179 if 'chapuser' in self.dconf \
180 and ('chappassword' in self.dconf or 'chappassword_secret' in self.dconf):
181 self.chapuser = self.dconf['chapuser'].encode('utf-8')
182 if 'chappassword_secret' in self.dconf: 182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true
183 chappassword = util.get_secret(self.session, self.dconf['chappassword_secret'])
184 else:
185 chappassword = self.dconf['chappassword']
187 self.chappassword = chappassword.encode('utf-8')
189 self.incoming_chapuser = ""
190 self.incoming_chappassword = ""
191 if 'incoming_chapuser' in self.dconf \
192 and ('incoming_chappassword' in self.dconf or 'incoming_chappassword_secret' in self.dconf):
193 self.incoming_chapuser = self.dconf['incoming_chapuser'].encode('utf-8')
194 if 'incoming_chappassword_secret' in self.dconf: 194 ↛ 195line 194 didn't jump to line 195, because the condition on line 194 was never true
195 incoming_chappassword = util.get_secret(self.session, self.dconf['incoming_chappassword_secret'])
196 else:
197 incoming_chappassword = self.dconf['incoming_chappassword']
199 self.incoming_chappassword = incoming_chappassword.encode('utf-8')
201 self.port = DEFAULT_PORT
202 if 'port' in self.dconf and self.dconf['port']: 202 ↛ 203line 202 didn't jump to line 203, because the condition on line 202 was never true
203 try:
204 self.port = int(self.dconf['port'])
205 except:
206 raise xs_errors.XenError('ISCSIPort')
207 if self.port > MAXPORT or self.port < 1: 207 ↛ 208line 207 didn't jump to line 208, because the condition on line 207 was never true
208 raise xs_errors.XenError('ISCSIPort')
210 # For backwards compatibility
211 if 'usediscoverynumber' in self.dconf: 211 ↛ 212line 211 didn't jump to line 212, because the condition on line 211 was never true
212 self.discoverentry = self.dconf['usediscoverynumber']
214 self.multihomed = False
215 if 'multihomed' in self.dconf: 215 ↛ 216line 215 didn't jump to line 216, because the condition on line 215 was never true
216 if self.dconf['multihomed'] == "true":
217 self.multihomed = True
218 elif self.mpath == 'true': 218 ↛ 219line 218 didn't jump to line 219, because the condition on line 218 was never true
219 self.multihomed = True
221 if 'targetIQN' not in self.dconf or not self.dconf['targetIQN']: 221 ↛ 222line 221 didn't jump to line 222, because the condition on line 221 was never true
222 self._scan_IQNs()
223 raise xs_errors.XenError('ConfigTargetIQNMissing')
225 self.targetIQN = self.dconf['targetIQN']
227 self._attached = None
228 self._pathdict = None
229 self._adapter = None
230 self._devs = None
231 self._tgtidx = None
232 self._path = None
233 self._address = None
235 def _initPaths(self):
236 self._init_adapters()
237 # Generate a list of all possible paths
238 self._pathdict = {}
239 addrlist = []
240 rec = {}
241 key = "%s:%d" % (self.target, self.port)
242 rec['ipaddr'] = self.target
243 rec['port'] = self.port
244 rec['path'] = os.path.join("/dev/iscsi", self.targetIQN, \
245 key)
246 self._pathdict[key] = rec
247 util.SMlog("PATHDICT: key %s: %s" % (key, rec))
248 self._tgtidx = key
249 addrlist.append(key)
251 self._path = rec['path']
252 self._address = self.tgtidx
253 if not self.attached: 253 ↛ 254line 253 didn't jump to line 254, because the condition on line 253 was never true
254 return
256 if self.multihomed:
257 map = iscsilib.get_node_records(targetIQN=self.targetIQN)
258 for i in range(0, len(map)):
259 (portal, tpgt, iqn) = map[i]
260 (ipaddr, port) = iscsilib.parse_IP_port(portal)
261 if self.target != ipaddr: 261 ↛ 258line 261 didn't jump to line 258, because the condition on line 261 was never false
262 key = "%s:%s" % (ipaddr, port)
263 rec = {}
264 rec['ipaddr'] = ipaddr
265 rec['port'] = int(port)
266 rec['path'] = os.path.join("/dev/iscsi", self.targetIQN, \
267 key)
268 self._pathdict[key] = rec
269 util.SMlog("PATHDICT: key %s: %s" % (key, rec))
270 addrlist.append(key)
272 if not os.path.exists(self.path):
273 # Try to detect an active path in order of priority
274 for key in self.pathdict: 274 ↛ 281line 274 didn't jump to line 281, because the loop on line 274 didn't complete
275 if key in self.adapter:
276 self._tgtidx = key
277 self._path = self.pathdict[self.tgtidx]['path']
278 if os.path.exists(self.path): 278 ↛ 274line 278 didn't jump to line 274, because the condition on line 278 was never false
279 util.SMlog("Path found: %s" % self.path)
280 break
281 self._address = self.tgtidx
282 self._synchroniseAddrList(addrlist)
284 def _init_adapters(self) -> None:
285 # Generate a list of active adapters
286 ids = scsiutil._genHostList(ISCSI_PROCNAME)
287 util.SMlog(ids)
288 self._adapter = {}
289 for host in ids:
290 try:
291 targetIQN = iscsilib.get_targetIQN(host)
292 if targetIQN != self.targetIQN: 292 ↛ 293line 292 didn't jump to line 293, because the condition on line 292 was never true
293 continue
294 (addr, port) = iscsilib.get_targetIP_and_port(host)
295 entry = "%s:%s" % (addr, port)
296 self._adapter[entry] = host
297 except:
298 pass
299 self._devs = scsiutil.cacheSCSIidentifiers()
301 @override
302 def attach(self, sr_uuid) -> None:
303 self._mpathHandle()
305 multiTargets = False
307 npaths = 0
308 try:
309 pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
310 if pbdref: 310 ↛ 316line 310 didn't jump to line 316, because the condition on line 310 was never false
311 other_config = self.session.xenapi.PBD.get_other_config(pbdref)
312 multiTargets = util.sessions_less_than_targets(other_config, self.dconf)
313 except:
314 pass
316 if not self.attached or multiTargets: 316 ↛ 396line 316 didn't jump to line 396, because the condition on line 316 was never false
317 # Verify iSCSI target and port
318 if 'multihomelist' in self.dconf and 'multiSession' not in self.dconf:
319 targetlist = self.dconf['multihomelist'].split(',')
320 else:
321 targetlist = ['%s:%d' % (self.target, self.port)]
322 conn = False
323 for val in targetlist: 323 ↛ 333line 323 didn't jump to line 333, because the loop on line 323 didn't complete
324 (target, port) = iscsilib.parse_IP_port(val)
325 try:
326 util._testHost(target, int(port), 'ISCSITarget')
327 self.target = target
328 self.port = int(port)
329 conn = True
330 break
331 except:
332 pass
333 if not conn: 333 ↛ 334line 333 didn't jump to line 334, because the condition on line 333 was never true
334 raise xs_errors.XenError('ISCSITarget')
336 # Test and set the initiatorname file
337 iscsilib.ensure_daemon_running_ok(self.localIQN)
339 # Check to see if auto attach was set
340 if not iscsilib._checkTGT(self.targetIQN, tgt=self.target) or multiTargets: 340 ↛ 395line 340 didn't jump to line 395, because the condition on line 340 was never false
341 try:
342 iqn_map = []
343 if 'any' != self.targetIQN: 343 ↛ 352line 343 didn't jump to line 352, because the condition on line 343 was never false
344 try:
345 iqn_map = iscsilib.get_node_records(self.targetIQN)
346 except:
347 # Pass the exception that is thrown, when there
348 # are no nodes
349 pass
351 # Check our current target is in the map
352 portal = '%s:%d' % (self.target, self.port)
353 if len(iqn_map) == 0 or not any([x[0] for x in iqn_map if x[0] == portal]): 353 ↛ exit, 353 ↛ 3582 missed branches: 1) line 353 didn't run the list comprehension on line 353, 2) line 353 didn't jump to line 358, because the condition on line 353 was never false
354 iqn_map = iscsilib.discovery(self.target, self.port,
355 self.chapuser, self.chappassword,
356 self.targetIQN,
357 iscsilib.get_iscsi_interfaces())
358 if len(iqn_map) == 0: 358 ↛ 359line 358 didn't jump to line 359, because the condition on line 358 was never true
359 self._scan_IQNs()
360 raise xs_errors.XenError('ISCSIDiscovery',
361 opterr='check target settings')
362 for i in range(0, len(iqn_map)):
363 (portal, tpgt, iqn) = iqn_map[i]
364 try:
365 (ipaddr, port) = iscsilib.parse_IP_port(portal)
366 if not self.multihomed and ipaddr != self.target:
367 continue
368 util._testHost(ipaddr, int(port), 'ISCSITarget')
369 util.SMlog("Logging in to [%s:%s]" % (ipaddr, port))
370 iscsilib.login(portal, iqn, self.chapuser,
371 self.chappassword,
372 self.incoming_chapuser,
373 self.incoming_chappassword,
374 self.mpath == "true")
375 npaths = npaths + 1
376 except Exception as e:
377 # Exceptions thrown in login are acknowledged,
378 # the rest of exceptions are ignored since some of the
379 # paths in multipath may not be reachable
380 if str(e).startswith('ISCSI login'):
381 raise
382 else:
383 pass
385 if not iscsilib._checkTGT(self.targetIQN, tgt=self.target): 385 ↛ 386line 385 didn't jump to line 386, because the condition on line 385 was never true
386 raise xs_errors.XenError('ISCSIDevice', \
387 opterr='during login')
389 # Allow the devices to settle
390 time.sleep(5)
392 except util.CommandException as inst:
393 raise xs_errors.XenError('ISCSILogin', \
394 opterr='code is %d' % inst.code)
395 self.attached = True
396 self._initPaths()
397 util._incr_iscsiSR_refcount(self.targetIQN, sr_uuid)
398 IQNs = []
399 if "multiSession" in self.dconf:
400 for iqn in self.dconf['multiSession'].split("|"):
401 if len(iqn):
402 IQNs.append(iqn.split(',')[2])
403 else:
404 IQNs.append(self.targetIQN)
406 sessions = 0
407 paths = iscsilib.get_IQN_paths()
408 for path in paths: 408 ↛ 409line 408 didn't jump to line 409, because the loop on line 408 never started
409 try:
410 if util.get_single_entry(os.path.join(path, 'targetname')) in IQNs:
411 sessions += 1
412 util.SMlog("IQN match. Incrementing sessions to %d" % sessions)
413 except:
414 util.SMlog("Failed to read targetname path," \
415 + "iscsi_sessions value may be incorrect")
417 if pbdref: 417 ↛ 425line 417 didn't jump to line 425, because the condition on line 417 was never false
418 # Just to be safe in case of garbage left during crashes
419 # we remove the key and add it
420 self.session.xenapi.PBD.remove_from_other_config(
421 pbdref, "iscsi_sessions")
422 self.session.xenapi.PBD.add_to_other_config(
423 pbdref, "iscsi_sessions", str(sessions))
425 if 'SCSIid' in self.dconf: 425 ↛ exitline 425 didn't return from function 'attach', because the condition on line 425 was never false
426 if self.mpath == 'true': 426 ↛ 427line 426 didn't jump to line 427, because the condition on line 426 was never true
427 self.mpathmodule.refresh(self.dconf['SCSIid'], 0)
428 dev_path = os.path.join("/dev/disk/by-scsid", self.dconf['SCSIid'])
429 if not os.path.exists(dev_path):
430 raise xs_errors.XenError('ConfigSCSIid')
432 devs = os.listdir(dev_path)
433 for dev in devs:
434 realdev = os.path.realpath(os.path.join(dev_path, dev))
435 util.set_scheduler(os.path.basename(realdev))
437 @override
438 def detach(self, sr_uuid) -> None:
439 self.detach_and_delete(sr_uuid, delete=False)
441 def detach_and_delete(self, sr_uuid, delete=True) -> None:
442 keys = []
443 pbdref = None
444 try:
445 pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
446 except:
447 pass
448 if 'SCSIid' in self.dconf:
449 self.mpathmodule.reset(self.dconf['SCSIid'], explicit_unmap=True)
450 keys.append("mpath-" + self.dconf['SCSIid'])
452 # Remove iscsi_sessions and multipathed keys
453 if pbdref is not None:
454 if self.cmd == 'sr_detach':
455 keys += ["multipathed", "iscsi_sessions"]
456 for key in keys:
457 try:
458 self.session.xenapi.PBD.remove_from_other_config(pbdref, key)
459 except:
460 pass
462 if util._decr_iscsiSR_refcount(self.targetIQN, sr_uuid) != 0:
463 return
465 if self.direct and util._containsVDIinuse(self):
466 return
468 if iscsilib._checkTGT(self.targetIQN):
469 try:
470 iscsilib.logout(self.target, self.targetIQN, all=True)
471 if delete:
472 iscsilib.delete(self.targetIQN)
473 except util.CommandException as inst:
474 raise xs_errors.XenError('ISCSIQueryDaemon', \
475 opterr='error is %d' % inst.code)
476 if iscsilib._checkTGT(self.targetIQN):
477 raise xs_errors.XenError('ISCSIQueryDaemon', \
478 opterr='Failed to logout from target')
480 self.attached = False
482 @override
483 def create(self, sr_uuid, size) -> None:
484 # Check whether an SR already exists
485 SRs = self.session.xenapi.SR.get_all_records()
486 for sr in SRs:
487 record = SRs[sr]
488 sm_config = record["sm_config"]
489 if 'targetIQN' in sm_config and \
490 sm_config['targetIQN'] == self.targetIQN:
491 raise xs_errors.XenError('SRInUse')
492 self.attach(sr_uuid)
493 # Wait up to MAX_TIMEOUT for devices to appear
494 util.wait_for_path(self.path, MAX_TIMEOUT)
496 if self._loadvdis() > 0:
497 scanrecord = SR.ScanRecord(self)
498 scanrecord.synchronise()
499 try:
500 self.detach(sr_uuid)
501 except:
502 pass
503 self.sm_config = self.session.xenapi.SR.get_sm_config(self.sr_ref)
504 self.sm_config['disktype'] = 'Raw'
505 self.sm_config['datatype'] = 'ISCSI'
506 self.sm_config['target'] = self.target
507 self.sm_config['targetIQN'] = self.targetIQN
508 self.sm_config['multipathable'] = 'true'
509 self.session.xenapi.SR.set_sm_config(self.sr_ref, self.sm_config)
510 return
512 @override
513 def delete(self, sr_uuid) -> None:
514 self.detach(sr_uuid)
515 return
517 @override
518 def probe(self) -> str:
519 SRs = self.session.xenapi.SR.get_all_records()
520 Recs = {}
521 for sr in SRs:
522 record = SRs[sr]
523 sm_config = record["sm_config"]
524 if 'targetIQN' in sm_config and \
525 sm_config['targetIQN'] == self.targetIQN:
526 Recs[record["uuid"]] = sm_config
527 return self.srlist_toxml(Recs)
529 @override
530 def scan(self, sr_uuid) -> None:
531 if not self.passthrough:
532 if not self.attached:
533 raise xs_errors.XenError('SRUnavailable')
534 self.refresh()
535 time.sleep(2) # it seems impossible to tell when a scan's finished
536 self._loadvdis()
537 self.physical_utilisation = self.physical_size
538 for uuid, vdi in self.vdis.items():
539 if vdi.managed:
540 self.physical_utilisation += vdi.size
541 self.virtual_allocation = self.physical_utilisation
542 super(BaseISCSISR, self).scan(sr_uuid)
544 @override
545 def vdi(self, uuid) -> VDI.VDI:
546 return LUNperVDI.RAWVDI(self, uuid)
548 def _scan_IQNs(self):
549 # Verify iSCSI target and port
550 util._testHost(self.target, self.port, 'ISCSITarget')
552 # Test and set the initiatorname file
553 iscsilib.ensure_daemon_running_ok(self.localIQN)
555 map = iscsilib.discovery(self.target, self.port, self.chapuser,
556 self.chappassword,
557 interfaceArray=iscsilib.get_iscsi_interfaces())
558 map.append(("%s:%d" % (self.targetlist, self.port), "0", "*"))
559 self.print_entries(map)
561 def _attach_LUN_bylunid(self, lunid):
562 if not self.attached:
563 raise xs_errors.XenError('SRUnavailable')
564 connected = []
565 for val in self.adapter:
566 if val not in self.pathdict:
567 continue
568 rec = self.pathdict[val]
569 path = os.path.join(rec['path'], "LUN%s" % lunid)
570 realpath = os.path.realpath(path)
571 host = self.adapter[val]
572 l = [realpath, host, 0, 0, lunid]
574 addDevice = True
575 if realpath in self.devs:
576 # if the device is stale remove it before adding again
577 real_SCSIid = None
578 try:
579 real_SCSIid = scsiutil.getSCSIid(realpath)
580 except:
581 pass
583 if real_SCSIid is not None:
584 # make sure this is the same scsiid, if not remove the device
585 cur_scsibuspath = glob.glob('/dev/disk/by-scsibus/*-%s:0:0:%s' % (host, lunid))
586 cur_SCSIid = os.path.basename(cur_scsibuspath[0]).split("-")[0]
587 if cur_SCSIid != real_SCSIid:
588 # looks stale, remove it
589 scsiutil.scsi_dev_ctrl(l, "remove")
590 else:
591 util.SMlog("Not attaching LUNID %s for adapter %s" \
592 " since the device exists and the scsi id %s seems" \
593 " to be valid. " % (lunid, val, real_SCSIid))
594 addDevice = False
595 else:
596 # looks stale, remove it
597 scsiutil.scsi_dev_ctrl(l, "remove")
599 if addDevice:
600 # add the device
601 scsiutil.scsi_dev_ctrl(l, "add")
602 if not util.wait_for_path(path, MAX_LUNID_TIMEOUT):
603 util.SMlog("Unable to detect LUN attached to host on path [%s]" % path)
604 continue
605 connected.append(path)
606 return connected
608 def _attach_LUN_byserialid(self, serialid):
609 if not self.attached:
610 raise xs_errors.XenError('SRUnavailable')
611 connected = []
612 for val in self.adapter:
613 if val not in self.pathdict:
614 continue
615 rec = self.pathdict[val]
616 path = os.path.join(rec['path'], "SERIAL-%s" % serialid)
617 realpath = os.path.realpath(path)
618 if realpath not in self.devs:
619 if not util.wait_for_path(path, 5):
620 util.SMlog("Unable to detect LUN attached to host on serial path [%s]" % path)
621 continue
622 connected.append(path)
623 return connected
625 def _detach_LUN_bylunid(self, lunid, SCSIid):
626 if not self.attached:
627 raise xs_errors.XenError('SRUnavailable')
628 if self.mpath == 'true' and len(SCSIid):
629 self.mpathmodule.reset(SCSIid, explicit_unmap=True)
630 util.remove_mpathcount_field(self.session, self.host_ref, self.sr_ref, SCSIid)
631 for val in self.adapter:
632 if val not in self.pathdict:
633 continue
634 rec = self.pathdict[val]
635 path = os.path.join(rec['path'], "LUN%s" % lunid)
636 realpath = os.path.realpath(path)
637 if realpath in self.devs:
638 util.SMlog("Found key: %s" % realpath)
639 scsiutil.scsi_dev_ctrl(self.devs[realpath], 'remove')
640 # Wait for device to disappear
641 if not util.wait_for_nopath(realpath, MAX_LUNID_TIMEOUT):
642 util.SMlog("Device has not disappeared after %d seconds" % \
643 MAX_LUNID_TIMEOUT)
644 else:
645 util.SMlog("Device [%s,%s] disappeared" % (realpath, path))
647 def _attach_LUN_bySCSIid(self, SCSIid):
648 if not self.attached:
649 raise xs_errors.XenError('SRUnavailable')
651 path = self.mpathmodule.path(SCSIid)
652 if not util.pathexists(path):
653 self.refresh()
654 if not util.wait_for_path(path, MAX_TIMEOUT):
655 util.SMlog("Unable to detect LUN attached to host [%s]"
656 % path)
657 raise xs_errors.XenError('ISCSIDevice')
659 # This function queries the session for the attached LUNs
660 def _loadvdis(self):
661 count = 0
662 if not os.path.exists(self.path):
663 return 0
664 for file in filter(self.match_lun, util.listdir(self.path)):
665 vdi_path = os.path.join(self.path, file)
666 LUNid = file.replace("LUN", "")
667 uuid = scsiutil.gen_uuid_from_string(scsiutil.getuniqueserial(vdi_path))
668 obj = self.vdi(uuid)
669 obj._query(vdi_path, LUNid)
670 self.vdis[uuid] = obj
671 self.physical_size += obj.size
672 count += 1
673 return count
675 def refresh(self):
676 for val in self.adapter:
677 util.SMlog("Rescanning host adapter %s" % self.adapter[val])
678 scsiutil.rescan([self.adapter[val]])
680 # Helper function for LUN-per-VDI VDI.introduce
681 def _getLUNbySMconfig(self, sm_config):
682 if 'LUNid' not in sm_config:
683 raise xs_errors.XenError('VDIUnavailable')
684 LUNid = int(sm_config['LUNid'])
685 if not len(self._attach_LUN_bylunid(LUNid)):
686 raise xs_errors.XenError('VDIUnavailable')
687 return os.path.join(self.path, "LUN%d" % LUNid)
689 # This function takes an ISCSI device and populate it with
690 # a dictionary of available LUNs on that target.
691 def print_LUNs(self):
692 self.LUNs = {}
693 if os.path.exists(self.path):
694 dom0_disks = util.dom0_disks()
695 for file in util.listdir(self.path):
696 if file.find("LUN") != -1 and file.find("_") == -1:
697 vdi_path = os.path.join(self.path, file)
698 if os.path.realpath(vdi_path) in dom0_disks:
699 util.SMlog("Hide dom0 boot disk LUN")
700 else:
701 LUNid = file.replace("LUN", "")
702 obj = self.vdi(self.uuid)
703 obj._query(vdi_path, LUNid)
704 self.LUNs[obj.uuid] = obj
706 def print_entries(self, map):
707 dom = xml.dom.minidom.Document()
708 element = dom.createElement("iscsi-target-iqns")
709 dom.appendChild(element)
710 count = 0
711 for address, tpgt, iqn in map:
712 entry = dom.createElement('TGT')
713 element.appendChild(entry)
714 subentry = dom.createElement('Index')
715 entry.appendChild(subentry)
716 textnode = dom.createTextNode(str(count))
717 subentry.appendChild(textnode)
719 try:
720 # We always expect a port so this holds
721 # regardless of IP version
722 (addr, port) = address.rsplit(':', 1)
723 except:
724 addr = address
725 port = DEFAULT_PORT
726 subentry = dom.createElement('IPAddress')
727 entry.appendChild(subentry)
728 textnode = dom.createTextNode(str(addr))
729 subentry.appendChild(textnode)
731 if int(port) != DEFAULT_PORT:
732 subentry = dom.createElement('Port')
733 entry.appendChild(subentry)
734 textnode = dom.createTextNode(str(port))
735 subentry.appendChild(textnode)
737 subentry = dom.createElement('TargetIQN')
738 entry.appendChild(subentry)
739 textnode = dom.createTextNode(str(iqn))
740 subentry.appendChild(textnode)
741 count += 1
742 print(dom.toprettyxml(), file=sys.stderr)
744 def srlist_toxml(self, SRs):
745 dom = xml.dom.minidom.Document()
746 element = dom.createElement("SRlist")
747 dom.appendChild(element)
749 for val in SRs:
750 record = SRs[val]
751 entry = dom.createElement('SR')
752 element.appendChild(entry)
754 subentry = dom.createElement("UUID")
755 entry.appendChild(subentry)
756 textnode = dom.createTextNode(val)
757 subentry.appendChild(textnode)
759 subentry = dom.createElement("Target")
760 entry.appendChild(subentry)
761 textnode = dom.createTextNode(record['target'])
762 subentry.appendChild(textnode)
764 subentry = dom.createElement("TargetIQN")
765 entry.appendChild(subentry)
766 textnode = dom.createTextNode(record['targetIQN'])
767 subentry.appendChild(textnode)
768 return dom.toprettyxml()
770 def match_lun(self, s):
771 regex = re.compile("_")
772 if regex.search(s, 0):
773 return False
774 regex = re.compile("LUN")
775 return regex.search(s, 0)