infi.storagemodel是github上的一個開源的項目,鏈接如下:
https://github.com/Infinidat/infi.storagemodel
在其中的/infi/storagemodel/linux目錄下的LinuxStorageModel(UnixStorageModel)類中忽肛,需要獲取一個Sysfs()對象待德,這個對象是在同一個目錄下的sysfs.py實現(xiàn)的壳快,Sysfs對象內部有一個hctl對象外驱,這個hctl對象是什么呢冻河?
一瓣喊、HCTL參數
在linux中蚪拦,scsi有4個層級的尋址方案:
- SCSI adapter number [host]
- channel number [bus]
- id number [target]
- lun [lun]
解釋:
SCSI適配器編號通常是在計算機內部IO總線上的一個任意的號碼弃榨,這些適配器通常被叫做HBA(host bus adapter),SCSI適配器編號是由內核指定的箍铲,且從0開始增加雇卷。
每一個HBA都控制了一個或多個SCSI總線。
每個SCSI總線都連接多個SCSI設備颠猴,在SCSI中稱HBA為initiator关划,initiator和通常稱之為SCSI設備的targets進行通信。
在lsscsi命令的manpage中翘瓮,我們可以看到如下的解釋贮折,
Generic SCSI devices can also be accessed via the bsg driver in Linux. By default, the bsg driver's device node names are of the form '/dev/bsg/H:C:T:L'.
執(zhí)行l(wèi)sscsi可以看到如下顯示結果:
[0:0:0:0] disk VMware, VMware Virtual S 1.0 /dev/sda
[0:0:1:0] disk VMware, VMware Virtual S 1.0 /dev/sdb
[2:0:0:0] cd/dvd NECVMWar VMware IDE CDR10 1.00 /dev/sr0
前面的[0:0:1:0]四個數字就代表了這個scsi設備的hctl參數,分別是:
- host: SCSI hosts currently attached to the system.調用lsscsi -H可以看到所有host
- channel
- target:
- lun: 邏輯單元數
二资盅、獲取HCTL
獲取HCTL可以通過直接調用lsscsi调榄,更為有效的方法是直接給device發(fā)送ioctl的請求,在infi.stragemodel中使用的是后者:
跟蹤hctl的獲取方法呵扛,Sysfs類中調用了一個方法:
from infi.sgutils.sg_map import get_hctl_for_sd_device
#dev_path='/dev/sdb'
hctl = get_hctl_for_sd_device(dev_path)
這個方法的實現(xiàn):
def get_hctl_for_sd_device(device_path):
from ..ioctl import scsi_ioctl_get_idlun as _ioctl
#獲取一個SCSI_IDLUN對象
struct = _ioctl(device_path)
# http://tldp.org/HOWTO/SCSI-Generic-HOWTO/scsi_g_idlun.html
# "four_in_one" is made up as follows:
# (scsi_device_id | (lun << 8) | (channel << 16) | (host_no << 24))
host = (struct.four_in_one >> 24)
channel = (struct.four_in_one >> 16) & 0xFF
target = (struct.four_in_one) & 0xFF
lun = (struct.four_in_one >> 8) & 0xFF
result = HCTL(host, channel, target, lun)
return HCTL(host, channel, target, lun)
scsi_ioctl_get_idlun的實現(xiàn):
def scsi_ioctl_get_idlun(device_path):
from array import array
struct_cls = structures.SCSI_IDLUN
size = struct_cls.min_max_sizeof().max
buffer = array("B", [0]*size)
#調用下一個函數每庆,op是固定的0x5382,執(zhí)行結果回寫到buffer中
result = ioctl(device_path, opcodes.SCSI_IOCTL_GET_IDLUN, buffer)
struct = struct_cls.create_from_string(buffer)
#返回SCSI_IDLUN(four_in_one=1, host_unique_id=0)
return struct
def ioctl(device_path, op_number, buffer=None):
#打開/dev/sdb,拿到句柄
fd = os.open(device_path, os.O_RDONLY | os.O_NONBLOCK)
try:
from fcntl import ioctl as _ioctl
args = [fd, op_number,]
if buffer is not None:
args.extend([buffer, True])
#args=[3, 21378, array('B', [0, 0, 0, 0, 0, 0, 0, 0]), True]
return _ioctl(*args)
finally:
os.close(fd)
scsi_ioctl_get_idlun返回兩個參數今穿,four_in_one和host_unique_id,這個four in one的參數其實就是我們需要的hctl參數缤灵,返回去看get_hctl_for_sd_device函數是怎么解析這個four in one的:
host = (struct.four_in_one >> 24)
channel = (struct.four_in_one >> 16) & 0xFF
target = (struct.four_in_one) & 0xFF
lun = (struct.four_in_one >> 8) & 0xFF
result = HCTL(host, channel, target, lun)
可以看出這個參數是一個4*8=32位的int型變量,host是第一個8位荣赶,channel是第二個8位凤价,lun是第三個8位,target是第4個8位拔创,lsscsi出來的[0:0:1:0]對應的four_in_one參數為:0|0|0|1利诺,這個32位的二進制數的十進制值為1。