Coverage for drivers/BaseISCSI.py : 29%

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