mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			671 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			671 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/python
 | |
| # Copyright (C) 2006-2007 XenSource Ltd.
 | |
| # Copyright (C) 2008-2009 Citrix Ltd.
 | |
| #
 | |
| # This program is free software; you can redistribute it and/or modify 
 | |
| # it under the terms of the GNU Lesser General Public License as published 
 | |
| # by the Free Software Foundation; version 2.1 only.
 | |
| #
 | |
| # This program is distributed in the hope that it will be useful, 
 | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 | |
| # GNU Lesser General Public License for more details.
 | |
| #
 | |
| # ISCSISR: ISCSI software initiator SR driver
 | |
| #
 | |
| 
 | |
| import SR, VDI, SRCommand, util
 | |
| import statvfs, time, LUNperVDI
 | |
| import os, socket, sys, re, glob
 | |
| import xml.dom.minidom
 | |
| import shutil, xmlrpclib
 | |
| import scsiutil, iscsilib
 | |
| import xs_errors, errno
 | |
| 
 | |
| CAPABILITIES = ["SR_PROBE","VDI_CREATE","VDI_DELETE","VDI_ATTACH",
 | |
|                 "VDI_DETACH", "VDI_INTRODUCE"]
 | |
| 
 | |
| CONFIGURATION = [ [ 'target', 'IP address or hostname of the iSCSI target (required)' ], \
 | |
|                   [ 'targetIQN', 'The IQN of the target LUN group to be attached (required)' ], \
 | |
|                   [ 'chapuser', 'The username to be used during CHAP authentication (optional)' ], \
 | |
|                   [ 'chappassword', 'The password to be used during CHAP authentication (optional)' ], \
 | |
|                   [ 'port', 'The network port number on which to query the target (optional)' ], \
 | |
|                   [ 'multihomed', 'Enable multi-homing to this target, true or false (optional, defaults to same value as host.other_config:multipathing)' ] ]
 | |
| 
 | |
| DRIVER_INFO = {
 | |
|     'name': 'iSCSI',
 | |
|     'description': 'Base ISCSI SR driver, provides a LUN-per-VDI. Does not support creation of VDIs but accesses existing LUNs on a target.',
 | |
|     'vendor': 'Citrix Systems Inc',
 | |
|     'copyright': '(C) 2008 Citrix Systems Inc',
 | |
|     'driver_version': '1.0',
 | |
|     'required_api_version': '1.0',
 | |
|     'capabilities': CAPABILITIES,
 | |
|     'configuration': CONFIGURATION
 | |
|     }
 | |
| 
 | |
| INITIATORNAME_FILE = '/etc/iscsi/initiatorname.iscsi'
 | |
| SECTOR_SHIFT = 9
 | |
| DEFAULT_PORT = 3260
 | |
| # 2^16 Max port number value
 | |
| MAXPORT = 65535
 | |
| MAX_TIMEOUT = 15
 | |
| MAX_LUNID_TIMEOUT = 60
 | |
| ISCSI_PROCNAME = "iscsi_tcp"
 | |
| 
 | |
| class ISCSISR(SR.SR):
 | |
|     """ISCSI storage repository"""
 | |
|     def handles(type):
 | |
|         if type == "iscsi":
 | |
|             return True
 | |
|         return False
 | |
|     handles = staticmethod(handles)
 | |
| 
 | |
|     def _synchroniseAddrList(self, addrlist):
 | |
|         if not self.multihomed:
 | |
|             return
 | |
|         change = False
 | |
|         if not self.dconf.has_key('multihomelist'):
 | |
|             change = True
 | |
|             self.mlist = []
 | |
|             mstr = ""
 | |
|         else:
 | |
|             self.mlist = self.dconf['multihomelist'].split(',')
 | |
|             mstr = self.dconf['multihomelist']
 | |
|         for val in addrlist:
 | |
|             if not val in self.mlist:
 | |
|                 self.mlist.append(val)
 | |
|                 if len(mstr):
 | |
|                     mstr += ","
 | |
|                 mstr += val
 | |
|                 change = True
 | |
|         if change:
 | |
|             pbd = None
 | |
|             try:
 | |
|                 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
 | |
|                 if pbd <> None:
 | |
|                     device_config = self.session.xenapi.PBD.get_device_config(pbd)
 | |
|                     device_config['multihomelist'] = mstr
 | |
|                     self.session.xenapi.PBD.set_device_config(pbd, device_config)
 | |
|             except:
 | |
|                 pass
 | |
|                 
 | |
| 
 | |
|     @util.transformpasswords
 | |
|     def load(self, sr_uuid):
 | |
|         self.sr_vditype = 'phy'
 | |
|         self.discoverentry = 0
 | |
|         self.default_vdi_visibility = False
 | |
|         
 | |
|         # Required parameters
 | |
|         if not self.dconf.has_key('target') or  not self.dconf['target']:
 | |
|             raise xs_errors.XenError('ConfigTargetMissing')
 | |
| 
 | |
|         # we are no longer putting hconf in the xml.
 | |
|         # Instead we pass a session and host ref and let the SM backend query XAPI itself
 | |
|         try:
 | |
|             if not self.dconf.has_key('localIQN'):
 | |
|                 self.localIQN = self.session.xenapi.host.get_other_config(self.host_ref)['iscsi_iqn']
 | |
|             else:
 | |
|                 self.localIQN = self.dconf['localIQN']
 | |
|         except:
 | |
|             raise xs_errors.XenError('ConfigISCSIIQNMissing')
 | |
|         
 | |
|         # Check for empty string
 | |
|         if not self.localIQN:
 | |
|             raise xs_errors.XenError('ConfigISCSIIQNMissing')
 | |
|         
 | |
|         try:
 | |
|             self.target = util._convertDNS(self.dconf['target'].split(',')[0])
 | |
|         except:
 | |
|             raise xs_errors.XenError('DNSError')
 | |
|         
 | |
|         self.targetlist = self.target
 | |
|         if self.dconf.has_key('targetlist'):
 | |
|             self.targetlist = self.dconf['targetlist']
 | |
| 
 | |
|         # Optional parameters
 | |
|         self.chapuser = ""
 | |
|         self.chappassword = ""
 | |
|         if self.dconf.has_key('chapuser') \
 | |
|                 and (self.dconf.has_key('chappassword') or self.dconf.has_key('chappassword_secret')):
 | |
|             self.chapuser = self.dconf['chapuser']
 | |
|             if self.dconf.has_key('chappassword_secret'):
 | |
|                 self.chappassword = util.get_secret(self.session, self.dconf['chappassword_secret'])
 | |
|             else:
 | |
|                 self.chappassword = self.dconf['chappassword']
 | |
| 
 | |
|         self.port = DEFAULT_PORT
 | |
|         if self.dconf.has_key('port') and self.dconf['port']:
 | |
|             try:
 | |
|                 self.port = long(self.dconf['port'])
 | |
|             except:
 | |
|                 raise xs_errors.XenError('ISCSIPort')
 | |
|         if self.port > MAXPORT or self.port < 1:
 | |
|             raise xs_errors.XenError('ISCSIPort')
 | |
| 
 | |
|         # For backwards compatibility
 | |
|         if self.dconf.has_key('usediscoverynumber'):
 | |
|             self.discoverentry = self.dconf['usediscoverynumber']
 | |
| 
 | |
|         self.multihomed = False
 | |
|         if self.dconf.has_key('multihomed'):
 | |
|             if self.dconf['multihomed'] == "true":
 | |
|                 self.multihomed = True
 | |
|         elif self.mpath == 'true':
 | |
|             self.multihomed = True
 | |
| 
 | |
|         if not self.dconf.has_key('targetIQN') or  not self.dconf['targetIQN']:
 | |
|             self._scan_IQNs()
 | |
|             raise xs_errors.XenError('ConfigTargetIQNMissing')
 | |
| 
 | |
|         self.targetIQN = self.dconf['targetIQN']
 | |
|         self.attached = False
 | |
|         try:
 | |
|             self.attached = iscsilib._checkTGT(self.targetIQN)
 | |
|         except:
 | |
|             pass
 | |
|         self._initPaths()
 | |
| 
 | |
|     def _initPaths(self):
 | |
|         self._init_adapters()
 | |
|         # Generate a list of all possible paths
 | |
|         self.pathdict = {}
 | |
|         addrlist = []
 | |
|         rec = {}
 | |
|         key = "%s:%d" % (self.target,self.port)
 | |
|         rec['ipaddr'] = self.target
 | |
|         rec['port'] = self.port
 | |
|         rec['path'] = os.path.join("/dev/iscsi",self.targetIQN,\
 | |
|                                    key)
 | |
|         self.pathdict[key] = rec
 | |
|         util.SMlog("PATHDICT: key %s: %s" % (key,rec))
 | |
|         self.tgtidx = key
 | |
|         addrlist.append(key)
 | |
| 
 | |
|         self.path = rec['path']
 | |
|         self.address = self.tgtidx
 | |
|         if not self.attached:
 | |
|             return
 | |
|         
 | |
|         if self.multihomed:
 | |
|             map = iscsilib.get_node_records(targetIQN=self.targetIQN)
 | |
|             for i in range(0,len(map)):
 | |
|                 (portal,tpgt,iqn) = map[i]
 | |
|                 (ipaddr,port) = portal.split(',')[0].split(':')
 | |
|                 if self.target != ipaddr:
 | |
|                     key = "%s:%s" % (ipaddr,port)
 | |
|                     rec = {}
 | |
|                     rec['ipaddr'] = ipaddr
 | |
|                     rec['port'] = long(port)
 | |
|                     rec['path'] = os.path.join("/dev/iscsi",self.targetIQN,\
 | |
|                                    key)
 | |
|                     self.pathdict[key] = rec
 | |
|                     util.SMlog("PATHDICT: key %s: %s" % (key,rec))
 | |
|                     addrlist.append(key)
 | |
| 
 | |
|         # Try to detect an active path in order of priority
 | |
|         for key in self.pathdict:
 | |
|             if self.adapter.has_key(key):
 | |
|                 self.tgtidx = key
 | |
|                 self.path = self.pathdict[self.tgtidx]['path']
 | |
|                 if os.path.exists(self.path):
 | |
|                     util.SMlog("Path found: %s" % self.path)
 | |
|                     break
 | |
|         self.address = self.tgtidx
 | |
|         self._synchroniseAddrList(addrlist)
 | |
| 
 | |
|     def _init_adapters(self):
 | |
|         # Generate a list of active adapters
 | |
|         ids = scsiutil._genHostList(ISCSI_PROCNAME)
 | |
|         util.SMlog(ids)
 | |
|         self.adapter = {}
 | |
|         for host in ids:
 | |
|             try:
 | |
|                 targetIQN = util.get_single_entry(glob.glob(\
 | |
|                     '/sys/class/iscsi_host/host%s/device/session*/iscsi_session*/targetname' % host)[0])
 | |
|                 if targetIQN != self.targetIQN:
 | |
|                     continue
 | |
|                 addr = util.get_single_entry(glob.glob(\
 | |
|                     '/sys/class/iscsi_host/host%s/device/session*/connection*/iscsi_connection*/persistent_address' % host)[0])
 | |
|                 port = util.get_single_entry(glob.glob(\
 | |
|                     '/sys/class/iscsi_host/host%s/device/session*/connection*/iscsi_connection*/persistent_port' % host)[0])
 | |
|                 entry = "%s:%s" % (addr,port)
 | |
|                 self.adapter[entry] = host
 | |
|             except:
 | |
|                 pass
 | |
|         self.devs = scsiutil.cacheSCSIidentifiers()
 | |
| 
 | |
|     def attach(self, sr_uuid):
 | |
|         self._mpathHandle()
 | |
| 
 | |
|         npaths=0
 | |
|         if not self.attached:
 | |
|             # Verify iSCSI target and port
 | |
|             if self.dconf.has_key('multihomelist') and not self.dconf.has_key('multiSession'):
 | |
|                 targetlist = self.dconf['multihomelist'].split(',')
 | |
|             else:
 | |
|                 targetlist = ['%s:%d' % (self.target,self.port)]
 | |
|             conn = False
 | |
|             for val in targetlist:
 | |
|                 (target,port) = val.split(':')
 | |
|                 try:
 | |
|                     util._testHost(target, long(port), 'ISCSITarget')
 | |
|                     self.target = target
 | |
|                     self.port = long(port)
 | |
|                     conn = True
 | |
|                     break
 | |
|                 except:
 | |
|                     pass
 | |
|             if not conn:
 | |
|                 raise xs_errors.XenError('ISCSITarget')
 | |
| 
 | |
|             # Test and set the initiatorname file
 | |
|             iscsilib.ensure_daemon_running_ok(self.localIQN)
 | |
| 
 | |
|             # Check to see if auto attach was set
 | |
|             if not iscsilib._checkTGT(self.targetIQN):
 | |
|                 try:
 | |
|                     map = iscsilib.discovery(self.target, self.port, self.chapuser, \
 | |
|                                              self.chappassword, targetIQN=self.targetIQN)
 | |
|                     iqn = ''
 | |
|                     if len(map) == 0:
 | |
|                         self._scan_IQNs()
 | |
|                         raise xs_errors.XenError('ISCSIDiscovery', 
 | |
|                                                  opterr='check target settings')
 | |
|                     for i in range(0,len(map)):
 | |
|                         (portal,tpgt,iqn) = map[i]
 | |
|                         try:
 | |
|                             (ipaddr,port) = portal.split(',')[0].split(':')
 | |
|                             if not self.multihomed and ipaddr != self.target:
 | |
|                                 continue
 | |
|                             util._testHost(ipaddr, long(port), 'ISCSITarget')
 | |
|                             util.SMlog("Logging in to [%s:%s]" % (ipaddr,port))
 | |
|                             iscsilib.login(portal, iqn, self.chapuser, self.chappassword)
 | |
|                             npaths = npaths + 1
 | |
|                         except:
 | |
|                             pass
 | |
| 
 | |
|                     if not iscsilib._checkTGT(self.targetIQN):
 | |
|                         raise xs_errors.XenError('ISCSIDevice', \
 | |
|                                                  opterr='during login')
 | |
|                 
 | |
|                     # Allow the devices to settle
 | |
|                     time.sleep(5)
 | |
|                 
 | |
|                 except util.CommandException, inst:
 | |
|                     raise xs_errors.XenError('ISCSILogin', \
 | |
|                                              opterr='code is %d' % inst.code)
 | |
|             self.attached = True
 | |
|         self._initPaths()
 | |
|         util._incr_iscsiSR_refcount(self.targetIQN, sr_uuid)
 | |
|         IQNs = []
 | |
|         if self.dconf.has_key("multiSession"):
 | |
|             IQNs = ""
 | |
|             for iqn in self.dconf['multiSession'].split("|"):
 | |
|                 if len(iqn): IQNs += iqn.split(',')[2]
 | |
|         else:
 | |
|             IQNs.append(self.targetIQN)
 | |
|         sessions = 0
 | |
|         paths = glob.glob(\
 | |
|                     '/sys/class/iscsi_host/host*/device/session*/iscsi_session*/targetname')
 | |
|         for path in paths:
 | |
|             try:
 | |
|                 if util.get_single_entry(path) in IQNs:
 | |
|                     sessions += 1
 | |
|                     util.SMlog("IQN match. Incrementing sessions to %d" % sessions)
 | |
|             except:
 | |
|                 util.SMlog("Failed to read targetname path," \
 | |
|                            + "iscsi_sessions value may be incorrect")
 | |
| 
 | |
|         try:
 | |
|             pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
 | |
|             if pbdref <> None:
 | |
|                 other_conf = self.session.xenapi.PBD.get_other_config(pbdref)
 | |
|                 other_conf['iscsi_sessions'] = str(sessions)
 | |
|                 self.session.xenapi.PBD.set_other_config(pbdref, other_conf)
 | |
|         except:
 | |
|             pass
 | |
| 
 | |
|         if self.mpath == 'true' and self.dconf.has_key('SCSIid'):
 | |
|             self.mpathmodule.refresh(self.dconf['SCSIid'],npaths)
 | |
| 
 | |
| 
 | |
|     def detach(self, sr_uuid):
 | |
|         keys = []
 | |
|         pbdref = None
 | |
|         try:
 | |
|             pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref)
 | |
|         except:
 | |
|             pass
 | |
|         if self.dconf.has_key('SCSIid'):
 | |
|             self.mpathmodule.reset(self.dconf['SCSIid'], True) # explicitly unmap
 | |
|             keys.append("mpath-" + self.dconf['SCSIid'])
 | |
| 
 | |
|         # Remove iscsi_sessions and multipathed keys
 | |
|         if pbdref <> None:
 | |
|             if self.cmd == 'sr_detach':
 | |
|                 keys += ["multipathed", "iscsi_sessions"]
 | |
|             for key in keys:
 | |
|                 try:
 | |
|                     self.session.xenapi.PBD.remove_from_other_config(pbdref, key)
 | |
|                 except:
 | |
|                     pass
 | |
| 
 | |
|         if util._decr_iscsiSR_refcount(self.targetIQN, sr_uuid) != 0:
 | |
|             return
 | |
| 
 | |
|         if self.direct and util._containsVDIinuse(self):
 | |
|             return
 | |
| 
 | |
|         if iscsilib._checkTGT(self.targetIQN):
 | |
|             try:
 | |
|                 iscsilib.logout(self.target, self.targetIQN, all=True)
 | |
|             except util.CommandException, inst:
 | |
|                     raise xs_errors.XenError('ISCSIQueryDaemon', \
 | |
|                           opterr='error is %d' % inst.code)
 | |
|             if iscsilib._checkTGT(self.targetIQN):
 | |
|                 raise xs_errors.XenError('ISCSIQueryDaemon', \
 | |
|                     opterr='Failed to logout from target')
 | |
| 
 | |
|         self.attached = False
 | |
| 
 | |
|     def create(self, sr_uuid, size):
 | |
|         # Check whether an SR already exists
 | |
|         SRs = self.session.xenapi.SR.get_all_records()
 | |
|         for sr in SRs:
 | |
|             record = SRs[sr]
 | |
|             sm_config = record["sm_config"]
 | |
|             if sm_config.has_key('targetIQN') and \
 | |
|                sm_config['targetIQN'] == self.targetIQN:
 | |
|                 raise xs_errors.XenError('SRInUse')
 | |
|         self.attach(sr_uuid)
 | |
|         # Wait up to MAX_TIMEOUT for devices to appear
 | |
|         util.wait_for_path(self.path, MAX_TIMEOUT)
 | |
|         
 | |
|         if self._loadvdis() > 0:
 | |
|             scanrecord = SR.ScanRecord(self)
 | |
|             scanrecord.synchronise()
 | |
|         try:
 | |
|             self.detach(sr_uuid)
 | |
|         except:
 | |
|             pass
 | |
|         self.sm_config = self.session.xenapi.SR.get_sm_config(self.sr_ref)
 | |
|         self.sm_config['disktype'] = 'Raw'
 | |
|         self.sm_config['datatype'] = 'ISCSI'
 | |
|         self.sm_config['target'] = self.target
 | |
|         self.sm_config['targetIQN'] = self.targetIQN
 | |
|         self.sm_config['multipathable'] = 'true'
 | |
|         self.session.xenapi.SR.set_sm_config(self.sr_ref, self.sm_config)
 | |
|         return
 | |
| 
 | |
|     def delete(self, sr_uuid):
 | |
|         self.detach(sr_uuid)
 | |
|         return
 | |
| 
 | |
|     def probe(self):
 | |
|         SRs = self.session.xenapi.SR.get_all_records()
 | |
|         Recs = {}
 | |
|         for sr in SRs:
 | |
|             record = SRs[sr]
 | |
|             sm_config = record["sm_config"]
 | |
|             if sm_config.has_key('targetIQN') and \
 | |
|                sm_config['targetIQN'] == self.targetIQN:
 | |
|                 Recs[record["uuid"]] = sm_config
 | |
|         return self.srlist_toxml(Recs)
 | |
|                 
 | |
|     
 | |
|     def scan(self, sr_uuid):
 | |
|         if not self.passthrough:
 | |
|             if not self.attached:
 | |
|                 raise xs_errors.XenError('SRUnavailable')
 | |
|             self.refresh()
 | |
|             time.sleep(2) # it seems impossible to tell when a scan's finished
 | |
|             self._loadvdis()
 | |
|             self.physical_utilisation = self.physical_size
 | |
|             for uuid, vdi in self.vdis.iteritems():
 | |
|                 if vdi.managed:
 | |
|                     self.physical_utilisation += vdi.size
 | |
|             self.virtual_allocation = self.physical_utilisation
 | |
|         return super(ISCSISR, self).scan(sr_uuid)
 | |
|                 
 | |
|     def vdi(self, uuid):
 | |
|         return LUNperVDI.RAWVDI(self, uuid)
 | |
| 
 | |
|     def _scan_IQNs(self):
 | |
|         # Verify iSCSI target and port
 | |
|         util._testHost(self.target, self.port, 'ISCSITarget')
 | |
| 
 | |
|         # Test and set the initiatorname file
 | |
|         iscsilib.ensure_daemon_running_ok(self.localIQN)
 | |
| 
 | |
|         map = iscsilib.discovery(self.target, self.port, self.chapuser, self.chappassword)
 | |
|         map.append(("%s:%d" % (self.targetlist,self.port),"0","*"))
 | |
|         self.print_entries(map)
 | |
| 
 | |
|     def _attach_LUN_bylunid(self, lunid):
 | |
|         if not self.attached:
 | |
|             raise xs_errors.XenError('SRUnavailable')
 | |
|         connected = []
 | |
|         for val in self.adapter:
 | |
|             if not self.pathdict.has_key(val):
 | |
|                 continue
 | |
|             rec = self.pathdict[val]
 | |
|             path = os.path.join(rec['path'],"LUN%s" % lunid)
 | |
|             util.SMlog("path: %s" % path)
 | |
|             realpath = os.path.realpath(path)
 | |
|             util.SMlog("realpath: %s" % realpath)
 | |
|             host = self.adapter[val]
 | |
|             if not self.devs.has_key(realpath):
 | |
|                 l = [realpath, host, 0, 0, lunid]
 | |
|                 scsiutil.scsi_dev_ctrl(l,"add")
 | |
|                 if not util.wait_for_path(path, MAX_LUNID_TIMEOUT):
 | |
|                     util.SMlog("Unable to detect LUN attached to host on path [%s]" % path)
 | |
|                     continue
 | |
|             else:
 | |
|                 # Verify that we are not seeing a stale LUN map
 | |
|                 try:
 | |
|                     real_SCSIid = scsiutil.getSCSIid(realpath)
 | |
|                     cur_scsibuspath = glob.glob('/dev/disk/by-scsibus/*-%s:0:0:%s' % (host,lunid))
 | |
|                     cur_SCSIid = os.path.basename(cur_scsibuspath[0]).split("-")[0]
 | |
|                     assert(cur_SCSIid == real_SCSIid)
 | |
|                 except:
 | |
|                     scsiutil.rescan([host])
 | |
|                     if not os.path.exists('/dev/disk/by-scsibus/%s-%s:0:0:%s' % \
 | |
|                                           (real_SCSIid,host,lunid)):
 | |
|                         util.SMlog("Unable to detect LUN attached to host after bus re-probe")
 | |
|                         continue
 | |
|             connected.append(path)
 | |
|         return connected
 | |
| 
 | |
|     def _attach_LUN_byserialid(self, serialid):
 | |
|         if not self.attached:
 | |
|             raise xs_errors.XenError('SRUnavailable')
 | |
|         connected = []
 | |
|         for val in self.adapter:
 | |
|             if not self.pathdict.has_key(val):
 | |
|                 continue
 | |
|             rec = self.pathdict[val]
 | |
|             path = os.path.join(rec['path'],"SERIAL-%s" % serialid)
 | |
|             realpath = os.path.realpath(path)
 | |
|             if not self.devs.has_key(realpath):
 | |
|                 if not util.wait_for_path(path, 5):
 | |
|                     util.SMlog("Unable to detect LUN attached to host on serial path [%s]" % path)
 | |
|                     continue
 | |
|             connected.append(path)
 | |
|         return connected
 | |
| 
 | |
|     def _detach_LUN_bylunid(self, lunid, SCSIid):
 | |
|         if not self.attached:
 | |
|             raise xs_errors.XenError('SRUnavailable')
 | |
|         if self.mpath == 'true' and len(SCSIid):
 | |
|             self.mpathmodule.reset(SCSIid, True)
 | |
|             util.remove_mpathcount_field(self.session, self.host_ref, self.sr_ref, SCSIid)
 | |
|         for val in self.adapter:
 | |
|             if not self.pathdict.has_key(val):
 | |
|                 continue
 | |
|             rec = self.pathdict[val]
 | |
|             path = os.path.join(rec['path'],"LUN%s" % lunid)            
 | |
|             realpath = os.path.realpath(path)
 | |
|             if self.devs.has_key(realpath):
 | |
| 		util.SMlog("Found key: %s" % realpath)
 | |
|                 scsiutil.scsi_dev_ctrl(self.devs[realpath], 'remove')
 | |
|                 # Wait for device to disappear
 | |
|                 if not util.wait_for_nopath(realpath, MAX_LUNID_TIMEOUT):
 | |
|                     util.SMlog("Device has not disappeared after %d seconds" % \
 | |
|                                MAX_LUNID_TIMEOUT)
 | |
|                 else:
 | |
|                     util.SMlog("Device [%s,%s] disappeared" % (realpath,path))
 | |
| 
 | |
|     def _attach_LUN_bySCSIid(self, SCSIid):
 | |
|         if not self.attached:
 | |
|             raise xs_errors.XenError('SRUnavailable')
 | |
| 
 | |
|         path = self.mpathmodule.path(SCSIid)
 | |
|         if not util.pathexists(path):
 | |
|             self.refresh()
 | |
|             if not util.wait_for_path(path, MAX_TIMEOUT):
 | |
|                 util.SMlog("Unable to detect LUN attached to host [%s]" \
 | |
|                            % path)
 | |
|                 return False
 | |
|         return True
 | |
| 
 | |
| 
 | |
|     # This function queries the session for the attached LUNs
 | |
|     def _loadvdis(self):
 | |
|         count = 0
 | |
|         if not os.path.exists(self.path):
 | |
|             return 0
 | |
|         for file in filter(self.match_lun, util.listdir(self.path)):
 | |
|             vdi_path = os.path.join(self.path,file)
 | |
|             LUNid = file.replace("LUN","")
 | |
|             uuid = scsiutil.gen_uuid_from_string(scsiutil.getuniqueserial(vdi_path))
 | |
|             obj = self.vdi(uuid)
 | |
|             obj._query(vdi_path, LUNid)
 | |
|             self.vdis[uuid] = obj
 | |
|             self.physical_size += obj.size
 | |
|             count += 1
 | |
|         return count
 | |
| 
 | |
|     def refresh(self):
 | |
|         for val in self.adapter:
 | |
|             util.SMlog("Rescanning host adapter %s" % self.adapter[val])
 | |
|             scsiutil.rescan([self.adapter[val]])
 | |
| 
 | |
|     # Helper function for LUN-per-VDI VDI.introduce
 | |
|     def _getLUNbySMconfig(self, sm_config):
 | |
|         if not sm_config.has_key('LUNid'):
 | |
|             raise xs_errors.XenError('VDIUnavailable')
 | |
|         LUNid = long(sm_config['LUNid'])
 | |
|         if not len(self._attach_LUN_bylunid(LUNid)):
 | |
|             raise xs_errors.XenError('VDIUnavailable')
 | |
|         return os.path.join(self.path,"LUN%d" % LUNid)
 | |
| 
 | |
|     def print_LUNs(self):
 | |
|         self.LUNs = {}
 | |
|         if os.path.exists(self.path):
 | |
|             for file in util.listdir(self.path):
 | |
|                 if file.find("LUN") != -1 and file.find("_") == -1:
 | |
|                     vdi_path = os.path.join(self.path,file)
 | |
|                     LUNid = file.replace("LUN","")
 | |
|                     obj = self.vdi(self.uuid)
 | |
|                     obj._query(vdi_path, LUNid)
 | |
|                     self.LUNs[obj.uuid] = obj
 | |
| 
 | |
|         dom = xml.dom.minidom.Document()
 | |
|         element = dom.createElement("iscsi-target")
 | |
|         dom.appendChild(element)
 | |
|         for uuid in self.LUNs:
 | |
|             val = self.LUNs[uuid]
 | |
|             entry = dom.createElement('LUN')
 | |
|             element.appendChild(entry)
 | |
|             
 | |
|             for attr in ('vendor', 'serial', 'LUNid', \
 | |
|                          'size', 'SCSIid'):
 | |
|                 try:
 | |
|                     aval = getattr(val, attr)
 | |
|                 except AttributeError:
 | |
|                     continue
 | |
|                 
 | |
|                 if aval:
 | |
|                     subentry = dom.createElement(attr)
 | |
|                     entry.appendChild(subentry)           
 | |
|                     textnode = dom.createTextNode(str(aval))
 | |
|                     subentry.appendChild(textnode)
 | |
| 
 | |
|         print >>sys.stderr,dom.toprettyxml()
 | |
| 
 | |
|     def print_entries(self, map):
 | |
|         dom = xml.dom.minidom.Document()
 | |
|         element = dom.createElement("iscsi-target-iqns")
 | |
|         dom.appendChild(element)
 | |
|         count = 0
 | |
|         for address,tpgt,iqn in map:
 | |
|             entry = dom.createElement('TGT')
 | |
|             element.appendChild(entry)
 | |
|             subentry = dom.createElement('Index')
 | |
|             entry.appendChild(subentry)
 | |
|             textnode = dom.createTextNode(str(count))
 | |
|             subentry.appendChild(textnode)
 | |
| 
 | |
|             try:
 | |
|                 addr,port = address.split(":")
 | |
|             except:
 | |
|                 addr = address
 | |
|                 port = DEFAULT_PORT
 | |
|             subentry = dom.createElement('IPAddress')
 | |
|             entry.appendChild(subentry)
 | |
|             textnode = dom.createTextNode(str(addr))
 | |
|             subentry.appendChild(textnode)
 | |
| 
 | |
|             if int(port) != DEFAULT_PORT:
 | |
|                 subentry = dom.createElement('Port')
 | |
|                 entry.appendChild(subentry)
 | |
|                 textnode = dom.createTextNode(str(port))
 | |
|                 subentry.appendChild(textnode)
 | |
| 
 | |
|             subentry = dom.createElement('TargetIQN')
 | |
|             entry.appendChild(subentry)           
 | |
|             textnode = dom.createTextNode(str(iqn))
 | |
|             subentry.appendChild(textnode)
 | |
|             count += 1
 | |
|         print >>sys.stderr,dom.toprettyxml()
 | |
| 
 | |
|     def srlist_toxml(self, SRs):
 | |
|         dom = xml.dom.minidom.Document()
 | |
|         element = dom.createElement("SRlist")
 | |
|         dom.appendChild(element)
 | |
| 
 | |
|         for val in SRs:
 | |
|             record = SRs[val]
 | |
|             entry = dom.createElement('SR')
 | |
|             element.appendChild(entry)
 | |
| 
 | |
|             subentry = dom.createElement("UUID")
 | |
|             entry.appendChild(subentry)
 | |
|             textnode = dom.createTextNode(val)
 | |
|             subentry.appendChild(textnode)
 | |
| 
 | |
|             subentry = dom.createElement("Target")
 | |
|             entry.appendChild(subentry)
 | |
|             textnode = dom.createTextNode(record['target'])
 | |
|             subentry.appendChild(textnode)
 | |
| 
 | |
|             subentry = dom.createElement("TargetIQN")
 | |
|             entry.appendChild(subentry)
 | |
|             textnode = dom.createTextNode(record['targetIQN'])
 | |
|             subentry.appendChild(textnode)
 | |
|         return dom.toprettyxml()
 | |
| 
 | |
|     def match_lun(self, s):
 | |
|         regex = re.compile("_")
 | |
|         if regex.search(s,0):
 | |
|             return False
 | |
|         regex = re.compile("LUN")
 | |
|         return regex.search(s, 0)    
 | |
|         
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     SRCommand.run(ISCSISR, DRIVER_INFO)
 | |
| else:
 | |
|     SR.registerSR(ISCSISR)
 |