infi.storagemodel是github上的一个开源的项目,链接如下:
https://github.com/Infinidat/infi.storagemodel
在其中的/infi/storagemodel/linux目录下的LinuxStorageModel(UnixStorageModel)类中,需要获取一个Sysfs()对象,这个对象是在同一个目录下的sysfs.py实现的,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'.
执行lsscsi可以看到如下显示结果:
[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发送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)
这个方法的实现:
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的实现:
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,执行结果回写到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。
网友评论