mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			469 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			469 lines
		
	
	
		
			14 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.
 | |
| #
 | |
| # Miscellaneous scsi utility functions
 | |
| #
 | |
| 
 | |
| import util, SR
 | |
| import os
 | |
| import re
 | |
| import xs_errors
 | |
| import base64
 | |
| import time
 | |
| import errno
 | |
| import glob
 | |
| 
 | |
| PREFIX_LEN = 4
 | |
| SUFFIX_LEN = 12
 | |
| SECTOR_SHIFT = 9
 | |
| 
 | |
| def gen_hash(st, len):
 | |
|     hs = 0
 | |
|     for i in st:
 | |
|         hs = ord(i) + (hs << 6) + (hs << 16) - hs
 | |
|     return str(hs)[0:len]
 | |
| 
 | |
| def gen_uuid_from_serial(iqn, serial):
 | |
|     if len(serial) < SUFFIX_LEN:
 | |
|         raise util.CommandException(1)
 | |
|     prefix = gen_hash(iqn, PREFIX_LEN)
 | |
|     suffix = gen_hash(serial, SUFFIX_LEN)
 | |
|     str = prefix.encode("hex") + suffix.encode("hex")
 | |
|     return str[0:8]+'-'+str[8:12]+'-'+str[12:16]+'-'+str[16:20]+'-'+str[20:32]
 | |
| 
 | |
| def gen_serial_from_uuid(iqn, uuid):
 | |
|     str = uuid.replace('-','')
 | |
|     prefix = gen_hash(iqn, PREFIX_LEN)
 | |
|     if str[0:(PREFIX_LEN * 2)].decode("hex") != prefix:
 | |
|         raise util.CommandException(1)
 | |
|     return str[(PREFIX_LEN * 2):].decode("hex")
 | |
| 
 | |
| def getsize(path):
 | |
|     dev = getdev(path)
 | |
|     sysfs = os.path.join('/sys/block',dev,'size')
 | |
|     size = 0
 | |
|     if os.path.exists(sysfs):
 | |
|         try:
 | |
|             f=open(sysfs, 'r')
 | |
|             size = (long(f.readline()) << SECTOR_SHIFT)
 | |
|             f.close()
 | |
|         except:
 | |
|             pass
 | |
|     return size
 | |
| 
 | |
| def getuniqueserial(path):
 | |
|     dev = getdev(path)
 | |
|     output = gen_rdmfile()
 | |
|     try:
 | |
|         cmd = ["md5sum"]
 | |
|         txt = util.pread3(cmd, getSCSIid(path))
 | |
|         return txt.split(' ')[0]
 | |
|     except:
 | |
|         return ''
 | |
| 
 | |
| def gen_uuid_from_string(str):
 | |
|     if len(str) < (PREFIX_LEN + SUFFIX_LEN):
 | |
|         raise util.CommandException(1)
 | |
|     return str[0:8]+'-'+str[8:12]+'-'+str[12:16]+'-'+str[16:20]+'-'+str[20:32]
 | |
| 
 | |
| def SCSIid_sanitise(str):
 | |
|     text = re.sub("^\s+","",str)
 | |
|     return re.sub("\s+","_",text)
 | |
| 
 | |
| def getSCSIid(path):
 | |
|     dev = rawdev(path)
 | |
|     cmd = ["scsi_id", "-g", "-s", "/block/%s" % dev]
 | |
|     return SCSIid_sanitise(util.pread2(cmd)[:-1])
 | |
| 
 | |
| def compareSCSIid_2_6_18(SCSIid, path):
 | |
|     serial = getserial(path)
 | |
|     len_serial = len(serial)
 | |
|     if (len_serial == 0 ) or (len_serial > (len(SCSIid) - 1)):
 | |
|         return False
 | |
|     list_SCSIid = list(SCSIid)
 | |
|     list_serial = list_SCSIid[1:(len_serial + 1)]
 | |
|     serial_2_6_18 = ''.join(list_serial)
 | |
|     if (serial == serial_2_6_18):
 | |
|         return True
 | |
|     else:
 | |
|         return False
 | |
| 
 | |
| def getserial(path):
 | |
|     dev = os.path.join('/dev',getdev(path))
 | |
|     try:
 | |
|         cmd = ["sginfo", "-s", dev]
 | |
|         text = re.sub("\s+","",util.pread2(cmd))
 | |
|     except:
 | |
|         raise xs_errors.XenError('EIO', \
 | |
|               opterr='An error occured querying device serial number [%s]' \
 | |
|                            % dev)
 | |
|     try:
 | |
|         return text.split("'")[1]
 | |
|     except:
 | |
|         return ''
 | |
| 
 | |
| def getmanufacturer(path):
 | |
|     cmd = ["sginfo", "-M", path]
 | |
|     try:
 | |
|         for line in filter(match_vendor, util.pread2(cmd).split('\n')):
 | |
|             return line.replace(' ','').split(':')[-1]
 | |
|     except:
 | |
|         return ''
 | |
| 
 | |
| def cacheSCSIidentifiers():
 | |
|     SCSI = {}
 | |
|     SYS_PATH = "/dev/disk/by-scsibus/*"
 | |
|     for node in glob.glob(SYS_PATH):
 | |
|         dev = os.path.realpath(node)
 | |
|         HBTL = os.path.basename(node).split("-")[-1].split(":")
 | |
|         line = "NONE %s %s %s %s 0 %s" % \
 | |
|                (HBTL[0],HBTL[1],HBTL[2],HBTL[3],dev)
 | |
|         ids = line.split()
 | |
|         SCSI[ids[6]] = ids
 | |
|     return SCSI
 | |
| 
 | |
| def scsi_dev_ctrl(ids, cmd):
 | |
|     f = -1
 | |
|     for i in range(0,10):
 | |
|         try:
 | |
|             str = "scsi %s-single-device %s %s %s %s" % \
 | |
|                   (cmd, ids[1],ids[2],ids[3],ids[4])
 | |
|             util.SMlog(str)
 | |
|             f=open('/proc/scsi/scsi', 'w')
 | |
|             print >>f, str
 | |
|             f.close()
 | |
|             return
 | |
|         except IOError, e:
 | |
|             util.SMlog("SCSI_DEV_CTRL: Failure, %s [%d]" % (e.strerror,e.errno))
 | |
|             if f >= 0:
 | |
|                 f.close()
 | |
|             if e.errno == errno.ENXIO:
 | |
|                 util.SMlog("Device has disappeared already")
 | |
|                 return
 | |
|             f = -1
 | |
|             time.sleep(6)
 | |
|             continue
 | |
|     raise xs_errors.XenError('EIO', \
 | |
|             opterr='An error occured during the scsi operation')
 | |
| 
 | |
| def getdev(path):
 | |
|     realpath = os.path.realpath(path)
 | |
|     if match_dm(realpath):
 | |
|         newpath = realpath.replace("/dev/mapper/","/dev/disk/by-id/scsi-")
 | |
|     else:
 | |
|         newpath = path
 | |
|     return os.path.realpath(newpath).split('/')[-1]
 | |
| 
 | |
| def rawdev(dev):
 | |
|     return re.sub("[0-9]*$","",getdev(dev))
 | |
| 
 | |
| def getSessionID(path):
 | |
|     for line in filter(match_session, util.listdir(path)):
 | |
|         return line.split('-')[-1]
 | |
| 
 | |
| def match_session(s):
 | |
|     regex = re.compile("^SESSIONID-")
 | |
|     return regex.search(s, 0)
 | |
| 
 | |
| def match_vendor(s):
 | |
|     regex = re.compile("^Vendor:")
 | |
|     return regex.search(s, 0)
 | |
| 
 | |
| def match_dm(s):
 | |
|     regex = re.compile("mapper/")
 | |
|     return regex.search(s, 0)    
 | |
|     
 | |
| def match_sd(s):
 | |
|     regex = re.compile("/dev/sd")
 | |
|     return regex.search(s, 0)
 | |
| 
 | |
| def _isSCSIdev(dev):
 | |
|     if match_dm(dev):
 | |
|         path = dev.replace("/dev/mapper/","/dev/disk/by-id/scsi-")
 | |
|     else:
 | |
|         path = dev
 | |
|     return match_sd(os.path.realpath(path))
 | |
| 
 | |
| def gen_rdmfile():
 | |
|     return "/tmp/%s" % util.gen_uuid()
 | |
| 
 | |
| def add_serial_record(session, sr_ref, devstring):
 | |
|     try:
 | |
|         conf = session.xenapi.SR.get_sm_config(sr_ref)
 | |
|         conf['devserial'] = devstring
 | |
|         session.xenapi.SR.set_sm_config(sr_ref, conf)
 | |
|     except:
 | |
|         pass
 | |
| 
 | |
| def get_serial_record(session, sr_ref):
 | |
|     try:
 | |
|         conf = session.xenapi.SR.get_sm_config(sr_ref)
 | |
|         return conf['devserial']
 | |
|     except:
 | |
|         return ""
 | |
| 
 | |
| def devlist_to_serialstring(devlist):
 | |
|     serial = ''
 | |
|     for dev in devlist:
 | |
|         try:
 | |
|             devserial = "scsi-%s" % getSCSIid(dev)
 | |
|             if not len(devserial) > 0:
 | |
|                 continue
 | |
|             if len(serial):
 | |
|                 serial += ','
 | |
|             serial += devserial
 | |
|         except:
 | |
|             pass
 | |
|     
 | |
|     return serial
 | |
| 
 | |
| def gen_synthetic_page_data(uuid):
 | |
|     # For generating synthetic page data for non-raw LUNs
 | |
|     # we set the vendor ID to XENSRC
 | |
|     # Note that the Page 80 serial number must be limited
 | |
|     # to 16 characters
 | |
|     page80 = ""
 | |
|     page80 += "\x00\x80"
 | |
|     page80 += "\x00\x12"
 | |
|     page80 += uuid[0:16]
 | |
|     page80 += "  "
 | |
|     
 | |
|     page83 = ""
 | |
|     page83 += "\x00\x83"
 | |
|     page83 += "\x00\x31"
 | |
|     page83 += "\x02\x01\x00\x2d"
 | |
|     page83 += "XENSRC  "
 | |
|     page83 += uuid
 | |
|     page83 += " "
 | |
|     return ["",base64.b64encode(page80),base64.b64encode(page83)]
 | |
|     
 | |
| def gen_raw_page_data(path):
 | |
|     default = ""
 | |
|     page80 = ""
 | |
|     page83 = ""
 | |
|     try:
 | |
|         cmd = ["sg_inq", "-r", path]
 | |
|         text = util.pread2(cmd)
 | |
|         default = base64.b64encode(text)
 | |
|             
 | |
|         cmd = ["sg_inq", "--page=0x80", "-r", path]
 | |
|         text = util.pread2(cmd)
 | |
|         page80 = base64.b64encode(text)
 | |
|             
 | |
|         cmd = ["sg_inq", "--page=0x83", "-r", path]
 | |
|         text = util.pread2(cmd)
 | |
|         page83 = base64.b64encode(text)
 | |
|     except:
 | |
|         pass
 | |
|     return [default,page80,page83]
 | |
| 
 | |
| def update_XS_SCSIdata(session, vdi_ref, vdi_uuid, data):
 | |
|         try:
 | |
|             session.xenapi.VDI.remove_from_xenstore_data(vdi_ref, "vdi-uuid")
 | |
|         except:
 | |
|             pass
 | |
|         
 | |
|         try:
 | |
|             session.xenapi.VDI.remove_from_xenstore_data(vdi_ref, "scsi/0x12/default")
 | |
|         except:
 | |
|             pass
 | |
|         
 | |
|         try:
 | |
|             session.xenapi.VDI.remove_from_xenstore_data(vdi_ref, "scsi/0x12/0x80")
 | |
|         except:
 | |
|             pass
 | |
| 
 | |
|         try:
 | |
|             session.xenapi.VDI.remove_from_xenstore_data(vdi_ref, "scsi/0x12/0x83")
 | |
|         except:
 | |
|             pass
 | |
| 
 | |
|         try:
 | |
|             session.xenapi.VDI.add_to_xenstore_data(vdi_ref, "vdi-uuid", vdi_uuid)
 | |
|             if len(data[0]):
 | |
|                 session.xenapi.VDI.add_to_xenstore_data(vdi_ref, "scsi/0x12/default", data[0])
 | |
| 
 | |
|             if len(data[1]):
 | |
|                 session.xenapi.VDI.add_to_xenstore_data(vdi_ref, "scsi/0x12/0x80", data[1])
 | |
| 
 | |
|             if len(data[2]):
 | |
|                 session.xenapi.VDI.add_to_xenstore_data(vdi_ref, "scsi/0x12/0x83", data[2])
 | |
|         except:
 | |
|             pass
 | |
| 
 | |
| def rescan(ids, scanstring='- - -'):
 | |
|     for id in ids:
 | |
|         refresh_HostID(id, scanstring)
 | |
| 
 | |
| def _genArrayIdentifier(dev):
 | |
|     try:
 | |
|         cmd = ["sg_inq", "--page=0xc8", "-r", dev]
 | |
|         id = util.pread2(cmd)
 | |
|         return id.encode("hex")[180:212]
 | |
|     except:
 | |
|         return ""
 | |
| 
 | |
| 
 | |
| def _genHostList(procname):
 | |
|     # loop through and check all adapters
 | |
|     ids = []
 | |
|     try:
 | |
|         for dir in util.listdir('/sys/class/scsi_host'):
 | |
|             filename = os.path.join('/sys/class/scsi_host',dir,'proc_name')
 | |
|             if os.path.exists(filename):
 | |
|                 f = open(filename, 'r')
 | |
|                 if f.readline().find(procname) != -1:
 | |
|                     ids.append(dir.replace("host",""))
 | |
|                 f.close()
 | |
|     except:
 | |
|         pass
 | |
|     return ids
 | |
| 
 | |
| def _genReverseSCSIidmap(SCSIid, pathname="scsibus"):
 | |
|     util.SMlog("map_by_scsibus: sid=%s" % SCSIid)
 | |
| 
 | |
|     devices = []
 | |
|     for link in glob.glob('/dev/disk/by-id/scsi-%s' % SCSIid):
 | |
|         devices.append(os.path.realpath(link))
 | |
|     return devices
 | |
| 
 | |
| def _genReverseSCSidtoLUNidmap(SCSIid):
 | |
|     devices = []
 | |
|     for link in glob.glob('/dev/disk/by-scsibus/%s-*' % SCSIid):
 | |
|         devices.append(link.split('-')[-1])
 | |
|     return devices
 | |
| 
 | |
| def _dosgscan():
 | |
|     regex=re.compile("([^:]*):\s+scsi([0-9]+)\s+channel=([0-9]+)\s+id=([0-9]+)\s+lun=([0-9]+)")
 | |
|     scan=util.pread2(["/usr/bin/sg_scan"]).split('\n')
 | |
|     sgs=[]
 | |
|     for line in scan:
 | |
|         m=regex.match(line)
 | |
|         if m:
 | |
|             device=m.group(1)
 | |
|             host=m.group(2)
 | |
|             channel=m.group(3)
 | |
|             sid=m.group(4)
 | |
|             lun=m.group(5)
 | |
|             sgs.append([device,host,channel,sid,lun])
 | |
|     return sgs
 | |
| 
 | |
| def refresh_HostID(HostID, scanstring):
 | |
|     LUNs = glob.glob('/sys/class/scsi_disk/%s*' % HostID)
 | |
|     li = []
 | |
|     for l in LUNs:
 | |
|         chan = re.sub(":[0-9]*$",'',os.path.basename(l))
 | |
|         if chan not in li:
 | |
|             li.append(chan)
 | |
| 
 | |
|     fullrescan = True
 | |
|     if len(li) and scanstring == "- - -":
 | |
|         fullrescan = False
 | |
|         for c in li:
 | |
|             if not refresh_scsi_channel(c):
 | |
|                 fullrescan = True
 | |
| 
 | |
|     if fullrescan:
 | |
|         util.SMlog("Rescanning HostID %s with %s" % (HostID, scanstring))
 | |
|         path = '/sys/class/scsi_host/host%s/scan' % HostID
 | |
|         if os.path.exists(path):
 | |
|             try:
 | |
|                 f=open(path, 'w')
 | |
|                 f.write('%s\n' % scanstring)
 | |
|                 f.close()
 | |
|             except:
 | |
|                 pass
 | |
|         # Host Bus scan issued, now try to detect channels
 | |
|         if util.wait_for_path("/sys/class/scsi_disk/%s*" % HostID, 5):
 | |
|             # At least one LUN is mapped
 | |
|             LUNs = glob.glob('/sys/class/scsi_disk/%s*' % HostID)
 | |
|             li = []
 | |
|             for l in LUNs:
 | |
|                 chan = re.sub(":[0-9]*$",'',os.path.basename(l))
 | |
|                 if chan not in li:
 | |
|                     li.append(chan)
 | |
|             for c in li:
 | |
|                 refresh_scsi_channel(c)
 | |
|     
 | |
| 
 | |
| def refresh_scsi_channel(channel):
 | |
|     DEV_WAIT = 5
 | |
|     util.SMlog("Refreshing channel %s" % channel)
 | |
|     util.wait_for_path('/dev/disk/by-scsibus/*-%s*' % channel, DEV_WAIT)
 | |
|     LUNs = glob.glob('/dev/disk/by-scsibus/*-%s*' % channel)
 | |
|     try:
 | |
|         rootdevs = util.dom0_disks()
 | |
|     except:
 | |
|         util.SMlog("Failed to query root disk, failing operation")
 | |
|         return False
 | |
|     
 | |
|     # a) Find a LUN to issue a Query LUNs command
 | |
|     li = []
 | |
|     Query = False
 | |
|     for lun in LUNs:
 | |
|         try:
 | |
|             hbtl = lun.split('-')[-1]
 | |
|             h = hbtl.split(':')
 | |
|             l=util.pread2(["/usr/bin/sg_luns","-q",lun]).split('\n')
 | |
|             li = []
 | |
|             for i in l:
 | |
|                 if len(i):
 | |
|                     li.append(int(i[0:4], 16))
 | |
|             util.SMlog("sg_luns query returned %s" % li)
 | |
|             Query = True
 | |
|             break
 | |
|         except:
 | |
|             pass
 | |
|     if not Query:
 | |
|         util.SMlog("Failed to detect or query LUN on Channel %s" % channel)
 | |
|         return False
 | |
| 
 | |
|     # b) Remove stale LUNs
 | |
|     current = glob.glob('/dev/disk/by-scsibus/*-%s:%s:%s*' % (h[0],h[1],h[2]))
 | |
|     for cur in current:
 | |
|         lunID = int(cur.split(':')[-1])
 | |
|         newhbtl = ['',h[0],h[1],h[2],str(lunID)]
 | |
|         if os.path.realpath(cur) in rootdevs:
 | |
|             # Don't touch the rootdev
 | |
|             if lunID in li: li.remove(lunID)
 | |
|             continue
 | |
|         
 | |
|         # Check if LUN is stale, and remove it
 | |
|         if not lunID in li:
 | |
|             util.SMlog("Stale LUN detected. Removing HBTL: %s" % newhbtl)
 | |
|             scsi_dev_ctrl(newhbtl,"remove")
 | |
|             util.wait_for_nopath(cur, DEV_WAIT)
 | |
|             continue
 | |
|         else:
 | |
|             li.remove(lunID)
 | |
| 
 | |
|         # Query SCSIid, check it matches, if not, re-probe
 | |
|         cur_SCSIid = os.path.basename(cur).split("-%s:%s:%s" % (h[0],h[1],h[2]))[0]
 | |
|         real_SCSIid = getSCSIid(cur)
 | |
|         if cur_SCSIid != real_SCSIid:
 | |
|             util.SMlog("HBTL %s does not match, re-probing" % newhbtl)
 | |
|             scsi_dev_ctrl(newhbtl,"remove")
 | |
|             util.wait_for_nopath(cur, DEV_WAIT)
 | |
|             scsi_dev_ctrl(newhbtl,"add")
 | |
|             util.wait_for_path('/dev/disk/by-scsibus/%s-%s' % (real_SCSIid,hbtl), DEV_WAIT)
 | |
|             pass
 | |
| 
 | |
|     # c) Probe for any LUNs that are not present in the system
 | |
|     for l in li:
 | |
|         newhbtl = ['',h[0],h[1],h[2],str(l)]
 | |
|         util.SMlog("Probing new HBTL: %s" % newhbtl)
 | |
|         scsi_dev_ctrl(newhbtl,"add")
 | |
|         util.wait_for_path('/dev/disk/by-scsibus/*-%s' % hbtl, DEV_WAIT)
 | |
| 
 | |
|     return True
 |