infi.storagemodel中如何获取存储设备的htcl参数


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。

你可能感兴趣的:(infi.storagemodel中如何获取存储设备的htcl参数)